Updated configuration

This commit is contained in:
2025-08-26 09:21:24 -03:00
parent 3b2ccc8417
commit c784df06fd
39 changed files with 1859 additions and 176 deletions

View File

@@ -21,6 +21,7 @@ in {
# FIXME(starter): add/edit as desired
./fonts.nix
./kitty.nix
./ghostty.nix
./git.nix
./ssh.nix
./shell
@@ -28,7 +29,7 @@ in {
inherit hostSpec;
services.ssh-agent.enable = true;
#services.ssh-agent.enable = true;
home = {
username = lib.mkDefault config.hostSpec.username;

View File

@@ -0,0 +1,9 @@
{
programs.ghostty = {
# Enable Ghostty (adjust package/settings in host/home files if needed)
enable = true;
# Example: set a specific package or settings here if desired
# package = pkgs.ghostty; # uncomment to pin package
# settings = { theme = "default"; };
};
}

View File

@@ -1,5 +1,5 @@
{
programs.kitty = {
enable = true;
};
};
}

View File

@@ -35,6 +35,7 @@
"ms-python.python"
"jock.svg"
"redhat.vscode-yaml"
"arrterian.nix-env-selector"
];
userSettings = {
# --- Privacy & Telemetry ---
@@ -73,6 +74,7 @@
"terraform.telemetry.enabled" = false;
"vsicons.dontShowNewVersionMessage" = true;
"workbench.welcomePage.walkthroughs.openOnInstall" = false;
"nixEnvSelector.useFlakes" = true;
};
};
}

View File

@@ -23,7 +23,8 @@
gnomeExtensions.caffeine
gnomeExtensions.clipboard-indicator
gnomeExtensions.launch-new-instance
gnomeExtensions.pop-shell
gnomeExtensions.paperwm
gnomeExtensions.just-perfection
];
# GNOME desktop settings via dconf
@@ -49,7 +50,8 @@
"caffeine@patapon.info"
"clipboard-indicator@tudmotu.com"
"launch-new-instance@gnome-shell-extensions.gcampax.github.com"
"pop-shell@system76.com"
"paperwm@paperwm.github.com"
"just-perfection@just-perfection"
];
};
@@ -70,21 +72,19 @@
experimental-features = ["scale-monitor-framebuffer" "variable-refresh-rate"];
};
# Unset conflicting Pop Shell keybindings to avoid conflicts with screen lock
"org/gnome/shell/extensions/pop-shell" = {
# Unset the HJKL directional bindings
tile-up = []; # Default was ['<Super>k']
tile-down = []; # Default was ['<Super>j']
tile-left = []; # Default was ['<Super>h']
tile-right = []; # Default was ['<Super>l']
# Unset the launcher binding that also uses Meta+L
activate-launcher = []; # Default was ['<Super>slash', '<Super>l']
# PaperWM extension settings placeholder
"org/gnome/shell/extensions/paperwm" = {
# Add any PaperWM-specific dconf keys here if needed
};
# Ensure the standard GNOME screen lock shortcut is active
"org/gnome/settings-daemon/plugins/media-keys" = {
screensaver = ["<Super>l"];
};
# Keybindings for window management
"org/gnome/desktop/wm/keybindings" = {
close = ["<Super>q"];
};
};
}

View File

@@ -656,5 +656,18 @@ in {
"org/gnome/desktop/peripherals/touchscreens/${duoConfig.secondary_touch_id}" = {
output = ["SDC" "0x419d" "0x00000000" duoConfig.secondary_display];
};
# Custom keybind for ZenBook display configuration
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/zenbook-displays" = {
name = "ZenBook Display Configuration";
command = "${zenbook-duo-tools}/bin/duo auto";
binding = "<Super>z";
};
"org/gnome/settings-daemon/plugins/media-keys" = {
custom-keybindings = [
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/zenbook-displays/"
];
};
};
}

View File

@@ -12,19 +12,15 @@
./keyring.nix
];
# Add network utility packages
# Add essential packages
home.packages = with pkgs; [
networkmanager # Network utility
uwsm # Universal Wayland Session Manager
networkmanagerapplet # NetworkManager system tray applet
];
# Configure UWSM for Hyprland session management
# Configure Hyprland session management
wayland.windowManager.hyprland = {
enable = true;
plugins = with pkgs.hyprlandPlugins; [
# Add any additional Hyprland plugins here
hyprspace
];
settings = {
# Set mod key
"$mod" = "SUPER";
@@ -35,35 +31,86 @@
"input:touchpad:middle_button_emulation" = false;
# Disable middle-click paste
"misc:middle_click_paste" = false;
# Enable 3-finger swipe to change workspaces
"gestures:workspace_swipe" = true;
"gestures:workspace_swipe_fingers" = 3;
"plugin:overview:reverseSwipe" = true;
# Built-in touchpad gesture configuration
"gestures" = {
# Enable workspace swipe gesture on touchpad (vertical for workspaces)
workspace_swipe = true;
# Use 3 fingers for workspace switching
workspace_swipe_fingers = 3;
# Distance for gesture recognition
workspace_swipe_distance = 300;
# Invert direction to match natural scrolling
workspace_swipe_invert = true;
# Enable workspace swiping from touchscreen edge
workspace_swipe_touch = true;
# Touchscreen invert direction
workspace_swipe_touch_invert = true;
# Create new workspace when swiping right on last workspace
workspace_swipe_create_new = true;
# Lock direction once threshold is reached
workspace_swipe_direction_lock = true;
# Direction lock threshold in pixels
workspace_swipe_direction_lock_threshold = 10;
};
# UWSM integration - finalize session with important variables
# Essential startup services
exec-once = [
"uwsm finalize HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP WAYLAND_DISPLAY"
# Start polkit authentication agent for secure authentication
"${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"
# Start NetworkManager applet for network management
"nm-applet --indicator"
];
# Layer rules for proper panel/bar integration
layerrule = [
# Waybar should be on top and reserve space
"blur,waybar"
"ignorezero,waybar"
];
# Keybinds
bind = [
# Launch terminal (kitty) via UWSM
"$mod,RETURN,exec,uwsm app -- kitty"
# Launch browser (zen) via UWSM
"$mod,B,exec,uwsm app -- zen"
# Launch terminal (kitty)
"$mod,RETURN,exec,kitty"
# Launch browser (zen)
"$mod,B,exec,zen"
# Close window
"$mod,Q,killactive"
# Logout - use UWSM stop
"$mod SHIFT,E,exec,uwsm stop"
# Logout
"$mod SHIFT,E,exit"
# Network utility (NetworkManager TUI) via UWSM
"$mod,N,exec,uwsm app -- kitty -e nmtui"
# Secrets manager (Seahorse - GNOME keyring GUI) via UWSM
"$mod,K,exec,uwsm app -- seahorse"
# Network utility (NetworkManager applet)
"$mod,N,exec,nm-applet"
# Secrets manager (Seahorse - GNOME keyring GUI)
"$mod,K,exec,seahorse"
# Overview - apply to all displays
"$mod,TAB,exec,hyprctl dispatch overview:toggle all"
# Standard window navigation
"$mod,Right,movefocus,r"
"$mod,Left,movefocus,l"
"$mod,Up,movefocus,u"
"$mod,Down,movefocus,d"
# Move windows
"$mod SHIFT,Right,movewindow,r"
"$mod SHIFT,Left,movewindow,l"
"$mod SHIFT,Up,movewindow,u"
"$mod SHIFT,Down,movewindow,d"
# Resize windows
"$mod,equal,resizeactive,10 0"
"$mod,minus,resizeactive,-10 0"
"$mod SHIFT,equal,resizeactive,0 10"
"$mod SHIFT,minus,resizeactive,0 -10"
# Toggle split direction
"$mod,V,togglesplit"
# Focus controls with vim keys
"$mod,H,movefocus,l"
"$mod,L,movefocus,r"
"$mod,K,movefocus,u"
"$mod,J,movefocus,d"
# Move active window to workspace
"$mod SHIFT,1,movetoworkspace,1"
@@ -77,24 +124,47 @@
"$mod SHIFT,9,movetoworkspace,9"
"$mod SHIFT,0,movetoworkspace,10"
# Cycle through workspaces
"$mod,Right,workspace,e+1"
"$mod,Left,workspace,e-1"
# Workspace switching with Meta+PageUp/PageDown
"META,Prior,workspace,e-1" # Meta+PageUp = previous workspace
"META,Next,workspace,e+1" # Meta+PageDown = next workspace
# Workspace switching with Super+Tab/Shift+Tab
"$mod,Tab,workspace,e+1" # Super+Tab = next workspace
"$mod SHIFT,Tab,workspace,e-1" # Super+Shift+Tab = previous workspace
];
# Mouse binds for workspace switching
# Mouse binds for window management
bindm = [
# Mod + scroll to change workspaces
# Mod + left click to move windows
"$mod,mouse:272,movewindow"
# Mod + right click to resize windows
"$mod,mouse:273,resizewindow"
];
# Scroll binds for workspace switching
# Scroll binds - super+shift for workspaces
binde = [
# Mod + scroll wheel to switch workspaces
"$mod,mouse_down,workspace,e+1"
"$mod,mouse_up,workspace,e-1"
# Super+Shift+Scroll: workspace switching
"$mod SHIFT,mouse_down,workspace,e+1"
"$mod SHIFT,mouse_up,workspace,e-1"
];
};
};
# Session variables for proper Wayland integration
home.sessionVariables = {
NIXOS_OZONE_WL = "1";
MOZ_ENABLE_WAYLAND = "1";
QT_QPA_PLATFORM = "wayland";
SDL_VIDEODRIVER = "wayland";
_JAVA_AWT_WM_NONREPARENTING = "1";
};
# Shell aliases for NetworkManager integration
home.shellAliases = {
# NetworkManager aliases for easier network management
nmcli-wifi-list = "nmcli dev wifi list";
nmcli-connect = "nmcli dev wifi connect";
nmcli-status = "nmcli general status";
nmcli-connections = "nmcli connection show";
};
}

View File

@@ -3,7 +3,7 @@
enable = true;
settings = {
general = {
lock_cmd = "pidof hyprlock || uwsm app -- hyprlock";
lock_cmd = "pidof hyprlock || hyprlock";
before_sleep_cmd = "loginctl lock-session";
after_sleep_cmd = "hyprctl dispatch dpms on";
};

View File

@@ -3,9 +3,9 @@
enable = true;
};
# Use UWSM to launch hyprlock
# Launch hyprlock
wayland.windowManager.hyprland.settings.bind = [
"$mod,L,exec,uwsm app -- hyprlock"
"$mod,L,exec,hyprlock"
];
home.packages = with pkgs; [

View File

@@ -3,6 +3,9 @@
home.packages = with pkgs; [
seahorse # GUI for managing keyrings and passwords
libsecret # Secret service API library
gnome-keyring # GNOME keyring daemon
polkit_gnome # Polkit authentication agent for GNOME
networkmanager # Ensure NetworkManager is available for password integration
];
# Session variables for proper keyring integration
@@ -15,17 +18,27 @@
SECRET_SERVICE_API = "1";
# Ensure proper D-Bus session bus integration
DBUS_SESSION_BUS_ADDRESS = "unix:path=$XDG_RUNTIME_DIR/bus";
# Enable password store integration
PASSWORD_STORE_ENABLE_EXTENSIONS = "true";
# Set keyring backend for applications that support it
KEYRING_BACKEND = "gnome-keyring";
# Ensure XDG runtime directory is available
XDG_RUNTIME_DIR = "/run/user/1000";
# Enable keyring unlock via PAM
GNOME_KEYRING_UNLOCK_PASSWORD = "1";
};
# Additional session setup for keyring integration
# Enable GNOME keyring service
services.gnome-keyring = {
enable = true;
components = ["pkcs11" "secrets" "ssh"];
};
# Enable additional Wayland session variables for Hyprland
wayland.windowManager.hyprland.settings.env = [
# Ensure XDG runtime directory is available
"XDG_RUNTIME_DIR,/run/user/1000"
# Enable keyring unlock via PAM
# Ensure all keyring environment variables are available to Hyprland
"SSH_AUTH_SOCK,$XDG_RUNTIME_DIR/keyring/ssh"
"GNOME_KEYRING_CONTROL,$XDG_RUNTIME_DIR/keyring"
"SECRET_SERVICE_API,1"
"KEYRING_BACKEND,gnome-keyring"
"GNOME_KEYRING_UNLOCK_PASSWORD,1"
];
}

View File

@@ -124,9 +124,9 @@
};
};
# Auto-start waybar with Hyprland via UWSM
# Auto-start waybar with Hyprland
wayland.windowManager.hyprland.settings.exec-once = [
"uwsm app -- waybar"
"waybar"
];
home.packages = with pkgs; [

View File

@@ -23,18 +23,18 @@
print_command = true;
layer = "overlay";
sort_order = "default";
term = "kitty"; #, SU
term = "kitty";
exec_search = false;
};
};
# Add wofi keybind to Hyprland: just $mod for drun (toggle behavior) with UWSM integration
# Add wofi keybind to Hyprland: just $mod for drun (toggle behavior)
wayland.windowManager.hyprland.settings.bindr = [
"$mod, SUPER_L, exec, pkill wofi || wofi --show drun --launch-prefix='uwsm app -- '"
"$mod, SUPER_L, exec, pkill wofi || wofi --show drun"
];
# Add wofi keybind to Hyprland: $mod+R for run (toggle behavior) with UWSM integration
# Add wofi keybind to Hyprland: $mod+R for run (toggle behavior)
wayland.windowManager.hyprland.settings.bind = [
"$mod,R,exec, pkill wofi || wofi --show run --launch-prefix='uwsm app -- '"
"$mod,R,exec, pkill wofi || wofi --show run"
];
home.packages = with pkgs; [

View File

@@ -171,15 +171,20 @@ in {
vfr = true;
};
# Auto-start the event-driven display management via UWSM
# Auto-start the event-driven display management
exec-once = [
"uwsm app -- ${zenbook-autostart}/bin/zenbook-autostart"
"${zenbook-autostart}/bin/zenbook-autostart"
# Additional trigger after a delay to handle race conditions
"sleep 5 && uwsm app -- ${zenbook-set-displays}/bin/zenbook-set-displays"
"sleep 5 && ${zenbook-set-displays}/bin/zenbook-set-displays"
# Monitor for display hotplug events (DRM/KMS events)
"uwsm app -- sh -c 'while ${pkgs.inotify-tools}/bin/inotifywait -e modify /sys/class/drm/*/status 2>/dev/null; do sleep 1 && ${zenbook-set-displays}/bin/zenbook-set-displays; done' &"
"sh -c 'while ${pkgs.inotify-tools}/bin/inotifywait -e modify /sys/class/drm/*/status 2>/dev/null; do sleep 1 && ${zenbook-set-displays}/bin/zenbook-set-displays; done' &"
# Monitor for graphics card state changes
"uwsm app -- sh -c '${pkgs.udev}/bin/udevadm monitor --udev --subsystem-match=drm | while read line; do if echo \"$line\" | grep -q \"change\"; then sleep 2 && ${zenbook-set-displays}/bin/zenbook-set-displays; fi; done' &"
"sh -c '${pkgs.udev}/bin/udevadm monitor --udev --subsystem-match=drm | while read line; do if echo \"$line\" | grep -q \"change\"; then sleep 2 && ${zenbook-set-displays}/bin/zenbook-set-displays; fi; done' &"
];
# Keybind to run zenbook-set-displays
bind = [
"$mod,Z,exec,${zenbook-set-displays}/bin/zenbook-set-displays"
];
};

View File

@@ -0,0 +1,52 @@
# Niri Configuration
This directory contains the niri desktop configuration, similar to the Hyprland setup but using the niri scrollable tiling Wayland compositor.
## Features
- **Scrollable tiling**: Niri's unique column-based scrollable tiling layout
- **Waybar integration**: Status bar with niri-specific modules
- **Wofi launcher**: Application launcher and runner
- **Swaylock screen locking**: Screen lock with swayidle integration
- **Keyring support**: GNOME keyring integration for password management
- **Screenshot support**: Grim + Slurp for screenshots
- **Media controls**: Volume and brightness control keybindings
## Files
- `default.nix`: Main niri configuration with keybindings and settings
- `waybar.nix`: Status bar configuration with niri modules
- `wofi.nix`: Application launcher configuration
- `swaylock.nix`: Screen lock configuration
- `swayidle.nix`: Idle management and auto-lock
- `keyring.nix`: GNOME keyring integration
## Key Bindings
- `Super + Return`: Launch terminal (kitty)
- `Super + B`: Launch browser (zen)
- `Super + Q`: Close window
- `Super + Shift + E`: Quit niri
- `Super`: Launch application launcher (wofi drun)
- `Super + D`: Launch run prompt (wofi run)
- `Super + L`: Lock screen (swaylock)
- `Super + 1-0`: Switch to workspace 1-10
- `Super + Shift + 1-0`: Move window to workspace 1-10
- `Super + Arrow Keys`: Navigate between columns/windows
- `Super + Ctrl + Arrow Keys`: Move windows/columns
- `Super + R`: Switch column width preset
- `Super + Shift + R`: Reset window height
- `Print`: Take screenshot
- `Super + Print`: Take area screenshot
## Usage
To use this niri configuration, import it in your home manager configuration:
```nix
imports = [
./common/optional/desktops/niri
];
```
Make sure the niri-flake is added to your system's flake inputs.

View File

@@ -0,0 +1,103 @@
# AGS Bar Configuration for Niri
This AGS configuration provides a comprehensive status bar for the Niri Wayland compositor with the following features:
## Features
### 🕒 Clock Widget
- Displays current time and date
- Updates every second
- Format: `HH:MM Mon DD`
### 🔊 Volume Control
- Shows current volume level and mute status
- **Left click**: Toggle mute
- Visual feedback with icons:
- 🔇 Muted
- 🔈 Low volume (0-33%)
- 🔉 Medium volume (34-66%)
- 🔊 High volume (67-100%)
### 🔋 Battery Indicator
- Shows battery percentage
- Charging status indicator
- Icons:
- 🔌 Charging
- 🔋 Battery levels
- 🪫 Low battery
### 🔵 Bluetooth Widget
- Shows Bluetooth power status
- **Left click**: Toggle Bluetooth on/off
- **Right click**: Auto-connect to paired devices
- Shows number of connected devices
- Icons:
- 🔵 Bluetooth enabled
- ⚫ Bluetooth disabled
- 📱 Connected devices counter
### 🌐 Network Widget
- Shows network connection status
- **Left click**: Toggle WiFi / show connection info
- Auto-connects to known networks
- Icons:
- 🌐 Wired connection
- 📶 WiFi strength indicator
- ❌ No connection
- Displays current WiFi SSID when connected
## Multi-Monitor Support
The bar automatically appears on all connected monitors using AGS's reactive monitor binding system. When monitors are added or removed, the bar will automatically adjust.
## Styling
The bar uses a modern dark theme with:
- Semi-transparent background
- Catppuccin-inspired color scheme
- Hover effects and smooth transitions
- Color-coded widget borders:
- 🟢 Volume (Green)
- 🟡 Battery (Yellow)
- 🔵 Bluetooth (Blue)
- 🔴 Network (Pink)
## Files
- `app.tsx` - Main application entry point
- `Bar.tsx` - Bar component with all widgets
- `style.css` - Styling and theme
- `package.json` - Project configuration
- `tsconfig.json` - TypeScript configuration
## Usage
The bar starts automatically with Niri via the `spawn-at-startup` configuration in the main Niri config. To manually restart:
```bash
pkill ags && ags run
```
## Customization
To customize the bar:
1. **Add new widgets**: Create functions in `Bar.tsx` and add them to the left/center/right sections
2. **Modify styling**: Edit `style.css` to change colors, spacing, and animations
3. **Change layout**: Modify the `centerbox` structure in the main Bar component
4. **Add keybindings**: Use Niri's keybinding system to interact with AGS widgets
## Dependencies
All required Astal libraries are automatically provided via the Nix configuration:
- `astal.battery` - Battery information
- `astal.bluetooth` - Bluetooth control
- `astal.network` - Network management
- `astal.wireplumber` - Audio control
## Notes
- The configuration uses modern AGS v2 syntax with JSX
- TypeScript errors in the editor are expected (AGS handles compilation)
- Network auto-connection relies on NetworkManager's saved connections
- Bluetooth auto-connection attempts to connect to paired devices

View File

@@ -0,0 +1,226 @@
import { Astal, Gtk, Gdk } from "ags/gtk4"
import { bind, Variable } from "ags"
import Wp from "gi://AstalWp"
import Battery from "gi://AstalBattery"
import Bluetooth from "gi://AstalBluetooth"
import Network from "gi://AstalNetwork"
import App from "ags/gtk4/app"
type BarProps = {
gdkmonitor: Gdk.Monitor
}
function Clock() {
const time = Variable("").poll(1000, 'date "+%H:%M %b %e"')
return (
<label
className="clock"
label={bind(time)}
/>
)
}
function VolumeWidget() {
const speaker = Wp.get_default()?.audio.defaultSpeaker!
function getVolumeIcon(volume: number, muted: boolean) {
if (muted) return "🔇"
if (volume > 66) return "🔊"
if (volume > 33) return "🔉"
if (volume > 0) return "🔈"
return "🔇"
}
return (
<box className="volume" spacing={8}>
<label
label={bind(speaker, "volume").as(v =>
getVolumeIcon(Math.floor(v * 100), speaker.mute)
)}
/>
<label
label={bind(speaker, "volume").as(v => `${Math.floor(v * 100)}%`)}
/>
<eventbox
onButtonPressEvent={(self, event) => {
if (event.button === 1) { // Left click
speaker.mute = !speaker.mute
}
}}
>
<label label="🎵" />
</eventbox>
</box>
)
}
function BatteryWidget() {
const bat = Battery.get_default()
function getBatteryIcon(percentage: number, charging: boolean) {
if (charging) return "🔌"
if (percentage > 80) return "🔋"
if (percentage > 60) return "🔋"
if (percentage > 40) return "🔋"
if (percentage > 20) return "🪫"
return "🪫"
}
return (
<box className="battery" spacing={8}>
<label
label={bind(bat, "percentage").as(p =>
getBatteryIcon(p, bat.charging)
)}
/>
<label
label={bind(bat, "percentage").as(p => `${Math.floor(p)}%`)}
/>
</box>
)
}
function BluetoothWidget() {
const bluetooth = Bluetooth.get_default()
return (
<box className="bluetooth" spacing={8}>
<label
label={bind(bluetooth, "isPowered").as(powered =>
powered ? "🔵" : "⚫"
)}
/>
<eventbox
onButtonPressEvent={async (self, event) => {
if (event.button === 1) { // Left click to toggle
bluetooth.isPowered = !bluetooth.isPowered
} else if (event.button === 3) { // Right click to connect to known devices
const devices = bluetooth.devices.filter(d => d.paired && !d.connected)
if (devices.length > 0) {
try {
await devices[0].connectDevice()
} catch (error) {
// Failed to connect to Bluetooth device
}
}
}
}}
>
<label
label={bind(bluetooth, "devices").as(devices => {
const connected = devices.filter(d => d.connected).length
return connected > 0 ? `📱${connected}` : "📱"
})}
/>
</eventbox>
</box>
)
}
function NetworkWidget() {
const network = Network.get_default()
function getNetworkIcon(wifi: any, wired: any) {
if (wired?.state === Network.DeviceState.ACTIVATED) {
return "🌐"
}
if (wifi?.state === Network.DeviceState.ACTIVATED) {
const strength = wifi.activeAccessPoint?.strength || 0
if (strength > 80) return "📶"
if (strength > 60) return "📶"
if (strength > 40) return "📶"
if (strength > 20) return "📶"
return "📶"
}
return "❌"
}
return (
<box className="network" spacing={8}>
<label
label={bind(network, "wifi").as(() =>
getNetworkIcon(network.wifi, network.wired)
)}
/>
<eventbox
onButtonPressEvent={async (self, event) => {
if (event.button === 1 && network.wifi) { // Left click
if (network.wifi.state === Network.DeviceState.ACTIVATED) {
// Already connected, show current connection
const ap = network.wifi.activeAccessPoint
if (ap) {
// Connected to network
}
} else {
// Try to connect to a known network
network.wifi.enabled = true
const accessPoints = network.wifi.accessPoints
const knownAPs = accessPoints.filter(ap => ap.ssid && ap.ssid.length > 0)
if (knownAPs.length > 0) {
try {
// This would typically require network manager secrets
// For auto-connection, NetworkManager should handle this
await network.wifi.connectToAccessPoint(knownAPs[0])
} catch (error) {
// Failed to connect to WiFi
}
}
}
}
}}
>
<label
label={bind(network, "wifi").as(() => {
if (network.wifi?.activeAccessPoint?.ssid) {
return network.wifi.activeAccessPoint.ssid
}
return "WiFi"
})}
/>
</eventbox>
</box>
)
}
function WorkspaceWidget() {
// Simple workspace widget showing current workspace
// Note: Niri doesn't have built-in workspace API, so this is a placeholder
return (
<box className="workspaces" spacing={8}>
<label label="󰍹 Workspaces" />
</box>
)
}
export default function Bar({ gdkmonitor }: BarProps) {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
return (
<window
className="bar"
gdkmonitor={gdkmonitor}
exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT}
application={App}
>
<centerbox className="bar-content">
<box className="left" spacing={16} halign={Gtk.Align.START}>
<Clock />
</box>
<box className="center" spacing={16} halign={Gtk.Align.CENTER}>
<WorkspaceWidget />
</box>
<box className="right" spacing={16} halign={Gtk.Align.END}>
<NetworkWidget />
<BluetoothWidget />
<VolumeWidget />
<BatteryWidget />
</box>
</centerbox>
</window>
)
}

View File

@@ -0,0 +1,19 @@
import Gtk from "gi://Gtk"
import Bar from "./Bar"
import { For, createBinding } from "ags"
import app from "ags/gtk4/app"
function main() {
const monitors = createBinding(app, "monitors")
return (
<For each={monitors} cleanup={(win) => (win as Gtk.Window).destroy()}>
{(monitor) => <Bar gdkmonitor={monitor} />}
</For>
)
}
app.start({
main,
css: "./style.css"
})

View File

@@ -0,0 +1,14 @@
{
"name": "ags-config",
"version": "1.0.0",
"description": "AGS configuration for niri desktop",
"type": "module",
"main": "app.tsx",
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
},
"dependencies": {
"ags": "*"
}
}

View File

@@ -0,0 +1,71 @@
/* Bar styling for Niri */
.bar {
background-color: rgba(30, 30, 46, 0.9);
color: #cdd6f4;
border-bottom: 2px solid #89b4fa;
padding: 0;
margin: 0;
}
.bar-content {
padding: 8px 16px;
}
.left, .center, .right {
padding: 4px 8px;
}
.clock {
font-weight: bold;
font-size: 14px;
color: #89b4fa;
}
.volume, .battery, .bluetooth, .network, .workspaces {
padding: 4px 8px;
border-radius: 8px;
background-color: rgba(69, 71, 90, 0.8);
transition: all 0.3s ease;
}
.volume:hover, .battery:hover, .bluetooth:hover, .network:hover, .workspaces:hover {
background-color: rgba(89, 91, 110, 0.9);
transform: scale(1.05);
}
.volume label, .battery label, .bluetooth label, .network label, .workspaces label {
font-size: 12px;
font-weight: 500;
}
/* Widget specific colors */
.volume {
border-left: 3px solid #a6e3a1;
}
.battery {
border-left: 3px solid #f9e2af;
}
.bluetooth {
border-left: 3px solid #89b4fa;
}
.network {
border-left: 3px solid #f38ba8;
}
.workspaces {
border-left: 3px solid #cba6f7;
}
/* Responsive design */
@media (max-width: 1200px) {
.bar-content {
padding: 6px 12px;
}
.volume, .battery, .bluetooth, .network, .workspaces {
padding: 3px 6px;
}
}

View File

@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"lib": ["ES2022"],
"allowJs": true,
"checkJs": true,
"declaration": false,
"strict": true,
"noImplicitAny": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"noEmit": true,
"skipLibCheck": true
},
"include": [
"**/*.ts",
"**/*.tsx",
"**/*.js"
]
}

View File

@@ -0,0 +1,33 @@
{
inputs,
pkgs,
...
}: {
# Add the AGS home manager module
imports = [inputs.ags.homeManagerModules.default];
programs.ags = {
enable = true;
# Symlink to ~/.config/ags
configDir = ./config;
# Additional packages and executables to add to GJS runtime
extraPackages = with pkgs; [
gtk3
glib
# Add Astal packages for enhanced functionality
astal.astal4
astal.io
astal.apps
astal.battery
astal.bluetooth
astal.network
astal.tray
astal.wireplumber
];
};
# Note: ags package is provided automatically by programs.ags.enable = true
# No need to add it to home.packages separately
}

View File

@@ -0,0 +1,205 @@
{pkgs, ...}: {
imports = [
# ./waybar.nix # Disabled in favor of AGS
./wofi.nix
./swaylock.nix
./swayidle.nix
./keyring.nix
./mako.nix
./ags
];
# Add network utility packages
home.packages = with pkgs; [
networkmanager # Network utility
wl-clipboard # Wayland clipboard
grim # Screenshot utility
slurp # Screen selection utility
mako # Notification daemon
brightnessctl # Brightness control
];
# Configure niri
programs.niri = {
settings = {
input = {
keyboard = {
xkb = {
layout = "us";
variant = "";
options = "grp:alt_shift_toggle";
};
};
touchpad = {
tap = true;
dwt = true;
natural-scroll = true;
click-method = "clickfinger";
middle-emulation = false;
};
mouse = {
natural-scroll = false;
accel-speed = 0.0;
accel-profile = "flat";
};
# Focus monitor on mouse hover, but not individual windows
focus-follows-mouse = {
enable = true;
max-scroll-amount = "0%";
};
};
outputs = {
# Place DP-4 on the left
"DP-4" = {
position = {
x = 1920;
y = 0;
};
};
# Place DP-5 to the right of it
"DP-5" = {
position = {
x = 0;
y = 0;
};
};
};
layout = {
gaps = 16;
center-focused-column = "never";
preset-column-widths = [
{proportion = 0.333;}
{proportion = 0.5;}
{proportion = 0.667;}
];
default-column-width = {proportion = 0.5;};
};
# Prefer dark theme
prefer-no-csd = true;
# Screenshot path
screenshot-path = "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png";
# Hotkey overlay
hotkey-overlay = {
skip-at-startup = true;
};
# Spawn programs at startup
spawn-at-startup = [
{command = ["mako"];}
{command = ["swayidle"];}
{command = ["ags" "run"];}
];
# Window rules
window-rules = [
{
matches = [{app-id = "^org\.gnome\.Nautilus$";}];
default-column-width = {proportion = 0.333;};
}
{
matches = [{app-id = "^pavucontrol$";}];
default-column-width = {proportion = 0.333;};
}
];
# Keybindings
binds = {
"Mod+B".action.spawn = ["zen"];
"Mod+Q".action.close-window = {};
"Mod+Shift+E".action.quit = {};
# Launcher
"Mod+D".action.spawn = ["sh" "-c" "pkill wofi || wofi --show drun"];
# Network utility (NetworkManager TUI)
"Mod+N".action.spawn = ["kitty" "-e" "nmtui"];
# Secrets manager (Seahorse - GNOME keyring GUI)
"Mod+K".action.spawn = ["seahorse"];
# Terminal (Ghostty)
"Mod+T".action.spawn = ["ghostty"];
# Workspace navigation
"Mod+Page_Up".action.focus-workspace-up = {};
"Mod+Page_Down".action.focus-workspace-down = {};
"Mod+1".action.focus-workspace = 1;
"Mod+2".action.focus-workspace = 2;
"Mod+3".action.focus-workspace = 3;
"Mod+4".action.focus-workspace = 4;
"Mod+5".action.focus-workspace = 5;
"Mod+6".action.focus-workspace = 6;
"Mod+7".action.focus-workspace = 7;
"Mod+8".action.focus-workspace = 8;
"Mod+9".action.focus-workspace = 9;
"Mod+0".action.focus-workspace = 10;
# Move window to workspace
"Mod+Shift+1".action.move-window-to-workspace = 1;
"Mod+Shift+2".action.move-window-to-workspace = 2;
"Mod+Shift+3".action.move-window-to-workspace = 3;
"Mod+Shift+4".action.move-window-to-workspace = 4;
"Mod+Shift+5".action.move-window-to-workspace = 5;
"Mod+Shift+6".action.move-window-to-workspace = 6;
"Mod+Shift+7".action.move-window-to-workspace = 7;
"Mod+Shift+8".action.move-window-to-workspace = 8;
"Mod+Shift+9".action.move-window-to-workspace = 9;
"Mod+Shift+0".action.move-window-to-workspace = 10;
# Window navigation
"Mod+Left".action.focus-column-left = {};
"Mod+Right".action.focus-column-right = {};
"Mod+Up".action.focus-window-up = {};
"Mod+Down".action.focus-window-down = {};
# Window movement
"Mod+Ctrl+Left".action.move-column-left = {};
"Mod+Ctrl+Right".action.move-column-right = {};
"Mod+Ctrl+Up".action.move-window-up = {};
"Mod+Ctrl+Down".action.move-window-down = {};
# Window resizing
"Mod+R".action.switch-preset-column-width = {};
"Mod+Shift+R".action.reset-window-height = {};
"Mod+F".action.set-window-width = "100%";
# Screenshots
"Print".action.spawn = ["grim"];
"Mod+Print".action.spawn = ["sh" "-c" "grim -g \"$(slurp)\""];
# Screen lock
"Mod+L".action.spawn = ["swaylock"];
# Brightness control
"XF86MonBrightnessUp".action.spawn = ["brightnessctl" "set" "5%+"];
"XF86MonBrightnessDown".action.spawn = ["brightnessctl" "set" "5%-"];
# Volume control
"XF86AudioRaiseVolume".action.spawn = ["wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"];
"XF86AudioLowerVolume".action.spawn = ["wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"];
"XF86AudioMute".action.spawn = ["wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"];
# Horizontal window scrolling with mod + scroll wheel
"Mod+WheelScrollDown".action.focus-column-right = {};
"Mod+WheelScrollUp".action.focus-column-left = {};
};
};
};
# Session variables for proper integration
home.sessionVariables = {
NIXOS_OZONE_WL = "1";
MOZ_ENABLE_WAYLAND = "1";
QT_QPA_PLATFORM = "wayland";
SDL_VIDEODRIVER = "wayland";
_JAVA_AWT_WM_NONREPARENTING = "1";
};
}

View File

@@ -0,0 +1,27 @@
{pkgs, ...}: {
# Additional keyring packages for user interaction
home.packages = with pkgs; [
seahorse # GUI for managing keyrings and passwords
libsecret # Secret service API library
];
# Session variables for proper keyring integration
home.sessionVariables = {
# SSH agent socket for SSH key management
SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/keyring/ssh";
# Control socket for GNOME Keyring daemon
GNOME_KEYRING_CONTROL = "$XDG_RUNTIME_DIR/keyring";
# Enable secret service D-Bus API
SECRET_SERVICE_API = "1";
# Ensure proper D-Bus session bus integration
DBUS_SESSION_BUS_ADDRESS = "unix:path=$XDG_RUNTIME_DIR/bus";
# Enable password store integration
PASSWORD_STORE_ENABLE_EXTENSIONS = "true";
# Set keyring backend for applications that support it
KEYRING_BACKEND = "gnome-keyring";
# Ensure XDG runtime directory is available
XDG_RUNTIME_DIR = "/run/user/1000";
# Enable keyring unlock via PAM
GNOME_KEYRING_UNLOCK_PASSWORD = "1";
};
}

View File

@@ -0,0 +1,38 @@
{pkgs, ...}: {
services.mako = {
enable = true;
settings = {
# Basic appearance
border-radius = 8;
border-size = 2;
width = 400;
height = 100;
padding = "10";
margin = "5";
default-timeout = 5000;
group-by = "app-name";
sort = "+time";
layer = "overlay";
anchor = "top-right";
# Action configuration
actions = true;
# Icon configuration
icon-path = "${pkgs.adwaita-icon-theme}/share/icons/Adwaita";
max-icon-size = 48;
# History configuration
max-history = 10;
# Markup configuration
markup = true;
format = "<b>%s</b>\\n%b";
};
};
home.packages = with pkgs; [
mako
libnotify # for notify-send command
];
}

View File

@@ -0,0 +1,39 @@
{pkgs, ...}: {
services.swayidle = {
enable = true;
events = [
{
event = "before-sleep";
command = "${pkgs.swaylock}/bin/swaylock -f";
}
{
event = "lock";
command = "${pkgs.swaylock}/bin/swaylock -f";
}
];
timeouts = [
{
timeout = 150;
command = "${pkgs.brightnessctl}/bin/brightnessctl -s set 10";
resumeCommand = "${pkgs.brightnessctl}/bin/brightnessctl -r";
}
{
timeout = 300;
command = "${pkgs.systemd}/bin/loginctl lock-session";
}
{
timeout = 330;
command = "${pkgs.niri}/bin/niri msg action power-off-monitors";
resumeCommand = "${pkgs.niri}/bin/niri msg action power-on-monitors";
}
{
timeout = 1800;
command = "${pkgs.systemd}/bin/systemctl suspend";
}
];
};
home.packages = with pkgs; [
swayidle
];
}

View File

@@ -0,0 +1,15 @@
{pkgs, ...}: {
programs.swaylock = {
enable = true;
settings = {
font-size = 24;
indicator-idle-visible = false;
indicator-radius = 100;
show-failed-attempts = true;
};
};
home.packages = with pkgs; [
swaylock
];
}

View File

@@ -0,0 +1,282 @@
{
config,
pkgs,
...
}: {
# Configure Stylix to target Waybar
stylix.targets.waybar.enable = true;
programs.waybar = {
enable = true;
settings = {
mainBar = {
layer = "top";
position = "top";
height = 30;
modules-left = ["niri/workspaces" "niri/mode" "niri/window"];
modules-center = ["clock"];
modules-right = ["network" "bluetooth" "pulseaudio" "battery" "tray" "custom/notification"];
"niri/workspaces" = {
disable-scroll = true;
all-outputs = true;
format = "{icon}";
format-icons = {
"1" = "1";
"2" = "2";
"3" = "3";
"4" = "4";
"5" = "5";
"6" = "6";
"7" = "7";
"8" = "8";
"9" = "9";
"10" = "10";
};
};
"niri/mode" = {
format = "<span style=\"italic\">{}</span>";
};
"niri/window" = {
format = "{}";
max-length = 50;
separate-outputs = true;
};
"clock" = {
tooltip-format = "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
format-alt = "{:%Y-%m-%d}";
format = "{:%H:%M}";
};
"network" = {
format-wifi = " {essid} ({signalStrength}%)";
format-ethernet = " {ipaddr}/{cidr}";
tooltip-format = " {ifname} via {gwaddr}";
format-linked = " {ifname} (No IP)";
format-disconnected = " Disconnected";
format-alt = "{ifname}: {ipaddr}/{cidr}";
};
"bluetooth" = {
format = " {status}";
format-disabled = "";
format-connected = " {device_alias}";
format-connected-battery = " {device_alias} {device_battery_percentage}%";
tooltip-format = "{controller_alias}\t{controller_address}\n\n{num_connections} connected";
tooltip-format-connected = "{controller_alias}\t{controller_address}\n\n{num_connections} connected\n\n{device_enumerate}";
tooltip-format-enumerate-connected = "{device_alias}\t{device_address}";
tooltip-format-enumerate-connected-battery = "{device_alias}\t{device_address}\t{device_battery_percentage}%";
};
"pulseaudio" = {
scroll-step = 5;
format = "{icon} {volume}%";
format-bluetooth = "{icon} {volume}% ";
format-bluetooth-muted = " {icon}";
format-muted = " {format_source}";
format-source = "{volume}% ";
format-source-muted = "";
format-icons = {
headphone = "";
hands-free = "";
headset = "";
phone = "";
portable = "";
car = "";
default = ["" "" ""];
};
on-click = "pavucontrol";
};
"battery" = {
states = {
good = 95;
warning = 30;
critical = 15;
};
format = "{icon} {capacity}%";
format-charging = " {capacity}%";
format-plugged = " {capacity}%";
format-alt = "{icon} {time}";
format-icons = ["" "" "" "" ""];
};
"tray" = {
icon-size = 21;
spacing = 10;
};
"custom/notification" = {
tooltip = false;
format = "{icon}";
format-icons = {
notification = "<span foreground='red'><sup></sup></span>";
none = "";
dnd-notification = "<span foreground='red'><sup></sup></span>";
dnd-none = "";
inhibited-notification = "<span foreground='red'><sup></sup></span>";
inhibited-none = "";
dnd-inhibited-notification = "<span foreground='red'><sup></sup></span>";
dnd-inhibited-none = "";
};
return-type = "json";
exec-if = "which swaync-client";
exec = "swaync-client -swb";
on-click = "swaync-client -t -sw";
on-click-right = "swaync-client -d -sw";
escape = true;
};
};
};
# Define the stylesheet using Adwaita-inspired design with Stylix variables
style = ''
* {
border: none;
border-radius: 0;
min-height: 0;
font-family: "Cantarell", sans-serif;
font-size: 13px;
font-weight: 500;
}
window#waybar {
background: linear-gradient(180deg, rgba(255,255,255,0.1) 0%, rgba(0,0,0,0.1) 100%);
background-color: #${config.lib.stylix.colors.base00};
color: #${config.lib.stylix.colors.base05};
border-bottom: 1px solid rgba(0,0,0,0.1);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition-property: background-color;
transition-duration: 200ms;
}
#workspaces {
margin: 0 8px;
}
#workspaces button {
padding: 4px 12px;
margin: 6px 2px;
color: #${config.lib.stylix.colors.base04};
background-color: transparent;
border-radius: 12px;
border: 1px solid rgba(255,255,255,0.1);
transition: all 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
min-width: 24px;
}
#workspaces button.active,
#workspaces button.focused {
background: linear-gradient(135deg, #${config.lib.stylix.colors.base0D} 0%, #${config.lib.stylix.colors.base0E} 100%);
color: #${config.lib.stylix.colors.base00};
border: 1px solid #${config.lib.stylix.colors.base0D};
box-shadow: 0 2px 8px rgba(0,0,0,0.2), inset 0 1px 0 rgba(255,255,255,0.2);
transform: translateY(-1px);
}
#workspaces button:hover {
background-color: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.2);
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
#clock,
#network,
#bluetooth,
#pulseaudio,
#battery,
#tray,
#custom-notification {
padding: 6px 12px;
margin: 6px 2px;
background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);
background-color: #${config.lib.stylix.colors.base01};
border: 1px solid rgba(255,255,255,0.1);
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1), inset 0 1px 0 rgba(255,255,255,0.1);
transition: all 200ms ease;
}
#clock:hover,
#network:hover,
#bluetooth:hover,
#pulseaudio:hover,
#battery:hover,
#tray:hover,
#custom-notification:hover {
background-color: #${config.lib.stylix.colors.base02};
border: 1px solid rgba(255,255,255,0.2);
transform: translateY(-1px);
box-shadow: 0 2px 6px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.2);
}
#mode {
background: linear-gradient(135deg, #${config.lib.stylix.colors.base08} 0%, #${config.lib.stylix.colors.base09} 100%);
color: #${config.lib.stylix.colors.base00};
padding: 6px 12px;
margin: 6px 2px;
border-radius: 12px;
border: 1px solid #${config.lib.stylix.colors.base08};
box-shadow: 0 2px 8px rgba(0,0,0,0.2), inset 0 1px 0 rgba(255,255,255,0.3);
font-weight: 600;
}
#window {
color: #${config.lib.stylix.colors.base05};
padding: 6px 12px;
margin: 6px 4px;
font-weight: 400;
opacity: 0.8;
}
#battery.warning {
color: #${config.lib.stylix.colors.base09};
background: linear-gradient(135deg, rgba(255,193,7,0.1) 0%, rgba(255,193,7,0.05) 100%);
border: 1px solid rgba(255,193,7,0.3);
}
#battery.critical {
color: #${config.lib.stylix.colors.base08};
background: linear-gradient(135deg, rgba(220,53,69,0.1) 0%, rgba(220,53,69,0.05) 100%);
border: 1px solid rgba(220,53,69,0.3);
animation: blink 1s ease-in-out infinite alternate;
}
@keyframes blink {
to {
background-color: rgba(220,53,69,0.2);
}
}
#network.disconnected {
color: #${config.lib.stylix.colors.base08};
background: linear-gradient(135deg, rgba(220,53,69,0.1) 0%, rgba(220,53,69,0.05) 100%);
border: 1px solid rgba(220,53,69,0.3);
}
#pulseaudio.muted {
color: #${config.lib.stylix.colors.base03};
opacity: 0.6;
}
#tray > .passive {
opacity: 0.7;
}
#tray > .needs-attention {
background-color: #${config.lib.stylix.colors.base08};
border: 1px solid #${config.lib.stylix.colors.base08};
animation: blink 1s ease-in-out infinite alternate;
}
'';
};
home.packages = with pkgs; [
waybar
];
}

View File

@@ -0,0 +1,37 @@
{pkgs, ...}: {
programs.wofi = {
enable = true;
settings = {
width = 600;
height = 400;
location = "center";
show = "drun";
prompt = "Search...";
filter_rate = 100;
allow_markup = true;
no_actions = true;
halign = "fill";
orientation = "vertical";
content_halign = "fill";
insensitive = true;
allow_images = true;
image_size = 40;
gtk_dark = true;
dynamic_lines = false;
matching = "contains";
hide_scroll = true;
print_command = true;
layer = "overlay";
sort_order = "default";
term = "kitty";
exec_search = false;
};
};
# Keybind: Mod+Return launches wofi only if not already running
programs.niri.settings.binds."Mod+Return".action.spawn = ["sh" "-c" "pgrep -x wofi || wofi --show drun"];
home.packages = with pkgs; [
wofi
];
}

View File

@@ -0,0 +1,229 @@
{
pkgs,
lib,
...
}: let
# Configuration variables from your script
preferred_resolution = "2880x1800";
refresh_rate = "120";
ui_scale = "1.5";
y_offset = "1200";
# Inline scripts adapted for niri
zenbook-top = pkgs.writeShellScriptBin "zenbook-top" ''
echo "Setting to top screen only"
${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0
${pkgs.niri}/bin/niri msg output eDP-2 off
'';
zenbook-bottom = pkgs.writeShellScriptBin "zenbook-bottom" ''
echo "Setting to bottom screen only"
${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0
${pkgs.niri}/bin/niri msg output eDP-1 off
'';
zenbook-both = pkgs.writeShellScriptBin "zenbook-both" ''
echo "Setting to both screens"
${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0
${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,${y_offset}
'';
zenbook-left-up = pkgs.writeShellScriptBin "zenbook-left-up" ''
echo "Setting to left-right layout"
${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0
${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position ${y_offset},0
'';
zenbook-right-up = pkgs.writeShellScriptBin "zenbook-right-up" ''
echo "Setting to right-left layout"
${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0
${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position ${y_offset},0
'';
zenbook-normal = pkgs.writeShellScriptBin "zenbook-normal" ''
# Check if lid is closed
lid_closed=false
if grep -q closed /proc/acpi/button/lid/*/state 2>/dev/null; then
lid_closed=true
fi
# Check if keyboard is attached (USB dock detection)
keyboard_attached=false
if ${pkgs.usbutils}/bin/lsusb | grep 0b05:1b2c; then
keyboard_attached=true
fi
if [ "$lid_closed" = true ] && [ "$keyboard_attached" = true ]; then
echo "Lid closed and keyboard attached disabling both screens"
${pkgs.niri}/bin/niri msg output eDP-1 off
${pkgs.niri}/bin/niri msg output eDP-2 off
elif [ "$lid_closed" = true ]; then
echo "Lid closed disabling top screen"
${pkgs.niri}/bin/niri msg output eDP-1 off
${pkgs.niri}/bin/niri msg output eDP-2 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0
elif [ "$keyboard_attached" = true ]; then
echo "Keyboard attached disabling bottom screen"
${pkgs.niri}/bin/niri msg output eDP-1 mode ${preferred_resolution}@${refresh_rate} scale ${ui_scale} position 0,0
${pkgs.niri}/bin/niri msg output eDP-2 off
else
echo "Normal mode enabling both screens"
${zenbook-both}/bin/zenbook-both
fi
'';
zenbook-toggle = pkgs.writeShellScriptBin "zenbook-toggle" ''
# Check current state and toggle
enabled_count=$(${pkgs.niri}/bin/niri msg outputs | grep -c "eDP.*" || echo "0")
if [ "$enabled_count" -eq 1 ]; then
${zenbook-both}/bin/zenbook-both
else
${zenbook-top}/bin/zenbook-top
fi
'';
# Event-driven display management (from your original script)
zenbook-watch-displays = pkgs.writeShellScriptBin "zenbook-watch-displays" ''
${zenbook-normal}/bin/zenbook-normal
while ${pkgs.inotify-tools}/bin/inotifywait -e attrib /dev/bus/usb/*/; do
${zenbook-normal}/bin/zenbook-normal
done
'';
zenbook-set-displays = pkgs.writeShellScriptBin "zenbook-set-displays" ''
sleep 1
${zenbook-normal}/bin/zenbook-normal
'';
# Enhanced lid monitoring with multiple detection methods
zenbook-lid-monitor = pkgs.writeShellScriptBin "zenbook-lid-monitor" ''
echo "Starting comprehensive lid monitoring..."
# Method 1: Monitor ACPI events
(${pkgs.acpi}/bin/acpi_listen | while read event; do
if echo "$event" | grep -q "button/lid"; then
echo "ACPI lid event detected: $event"
sleep 0.5 && ${zenbook-set-displays}/bin/zenbook-set-displays
fi
done) &
# Method 2: Poll lid state file directly
(while true; do
if [ -f /proc/acpi/button/lid/LID0/state ] || [ -f /proc/acpi/button/lid/LID/state ]; then
current_state=$(cat /proc/acpi/button/lid/*/state 2>/dev/null | head -1)
if [ "$current_state" != "$previous_state" ]; then
echo "Lid state changed: $current_state"
previous_state="$current_state"
sleep 0.5 && ${zenbook-set-displays}/bin/zenbook-set-displays
fi
fi
sleep 2
done) &
# Method 3: Monitor systemd logind events
(${pkgs.systemd}/bin/journalctl -f --user -u "*" | while read line; do
if echo "$line" | grep -i "lid\|suspend\|resume"; then
echo "Systemd event: $line"
sleep 1 && ${zenbook-set-displays}/bin/zenbook-set-displays
fi
done) &
wait
'';
# Auto-run display setup on startup
zenbook-autostart = pkgs.writeShellScriptBin "zenbook-autostart" ''
# Initial display setup
echo "Running initial display setup"
${zenbook-set-displays}/bin/zenbook-set-displays
# Wait a moment for system to stabilize
sleep 2
# Run display setup again to ensure proper configuration
echo "Running secondary display setup"
${zenbook-set-displays}/bin/zenbook-set-displays
# Start USB monitoring in background
echo "Starting USB monitoring for display changes"
${zenbook-watch-displays}/bin/zenbook-watch-displays &
# Start enhanced lid monitoring
echo "Starting enhanced lid monitoring"
${zenbook-lid-monitor}/bin/zenbook-lid-monitor &
'';
in {
# Zenbook dual-screen display configuration for niri
programs.niri.settings = {
outputs = {
"eDP-1" = {
mode = {
width = 2880;
height = 1800;
refresh = 120.0;
};
scale = 1.5;
position = {
x = 0;
y = 0;
};
variable-refresh-rate = true;
};
"eDP-2" = {
mode = {
width = 2880;
height = 1800;
refresh = 120.0;
};
scale = 1.5;
position = {
x = 0;
y = 1200;
};
variable-refresh-rate = true;
};
};
# Auto-start the event-driven display management
spawn-at-startup = [
{
command = ["${zenbook-autostart}/bin/zenbook-autostart"];
}
{
command = ["sh" "-c" "sleep 5 && ${zenbook-set-displays}/bin/zenbook-set-displays"];
}
{
command = ["sh" "-c" "while ${pkgs.inotify-tools}/bin/inotifywait -e modify /sys/class/drm/*/status 2>/dev/null; do sleep 1 && ${zenbook-set-displays}/bin/zenbook-set-displays; done"];
}
{
command = ["sh" "-c" "${pkgs.udev}/bin/udevadm monitor --udev --subsystem-match=drm | while read line; do if echo \"$line\" | grep -q \"change\"; then sleep 2 && ${zenbook-set-displays}/bin/zenbook-set-displays; fi; done"];
}
];
# Keybind to run zenbook-set-displays
binds."Mod+Z".action.spawn = ["${zenbook-set-displays}/bin/zenbook-set-displays"];
};
# Hook into swayidle for unlock/resume events (if using swayidle)
services.swayidle.timeouts = lib.mkAfter [
{
timeout = 1;
command = "${zenbook-set-displays}/bin/zenbook-set-displays";
resumeCommand = "${zenbook-set-displays}/bin/zenbook-set-displays";
}
];
# Add the scripts to home packages
home.packages = [
zenbook-top
zenbook-bottom
zenbook-both
zenbook-left-up
zenbook-right-up
zenbook-normal
zenbook-toggle
zenbook-watch-displays
zenbook-set-displays
zenbook-autostart
zenbook-lid-monitor
];
}

View File

@@ -1,6 +1,5 @@
{pkgs, ...}: {
imports = [
./lmstudio.nix
./obsidian.nix
];
@@ -12,5 +11,6 @@
blender
inkscape
libreoffice-qt
davinci-resolve
];
}

View File

@@ -1,5 +0,0 @@
{pkgs, ...}: {
home.packages = with pkgs; [
lmstudio
];
}