Skip to content

feat(noctalia): lid lock, AC-aware idle, 5min dimlock 10min suspend - #1651

Merged
shunkakinoki merged 5 commits into
mainfrom
update-noctalia
May 4, 2026
Merged

feat(noctalia): lid lock, AC-aware idle, 5min dimlock 10min suspend#1651
shunkakinoki merged 5 commits into
mainfrom
update-noctalia

Conversation

@shunkakinoki

@shunkakinoki shunkakinoki commented May 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • Lock screen on lid close via general.lockOnSuspend = true (noctalia calls lockAndSuspend() instead of suspend())
  • Disable idle entirely on AC power via a systemd user service (ac-idle-inhibit) that holds a systemd-inhibit --what=idle lock while /sys/class/power_supply/ACAD/online reads 1
  • On battery: screen off + lock at 5 min (screenOffTimeout/lockTimeout = 300), suspend at 10 min (suspendTimeout = 600)
  • Extracted AC inhibit script to ac-idle-inhibit.sh to satisfy shell-inline-check

Test plan

  • Closing lid locks screen
  • No idle timeouts fire while plugged in
  • Screen dims and locks at ~5 min on battery
  • System suspends at ~10 min on battery
  • shell-inline-check passes
  • shell-test passes

🤖 Generated with Claude Code


Summary by cubic

Lock the session on lid close and make idle power-aware. On AC, idle is inhibited; on battery, the display turns off and locks at 5 minutes and the system suspends at 10 minutes.

  • New Features

    • Enable lockOnSuspend to lock on lid close.
    • Add systemd user service ac-idle-inhibit that polls AC state every 2s and holds systemd-inhibit when online.
    • Set idle timeouts: screenOffTimeout/lockTimeout = 300s, suspendTimeout = 600s.
    • Minor UI tweaks in noctalia: add Workspaces widget, switch default font to Noto Sans, hide dock indicator, align GTK/icon themes with dark mode.
  • Bug Fixes

    • Extract AC-inhibit logic to ac-idle-inhibit.sh, add coverage entry, and fix Nix formatting.

Written for commit ca98d82. Summary will update on new commits.

@indent-zero

indent-zero Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor
PR Summary

Refreshes the noctalia-shell configuration on the Framework 13 (matic) host to tighten idle/lock behavior and tweak the bar layout. Adds a new systemd.user.services.ac-idle-inhibit unit + config/noctalia/ac-idle-inhibit.sh script intended to suppress noctalia's idle timers while on AC power, and configures explicit screen-off/lock/suspend timeouts plus lockOnSuspend = true. Latest commit (ca98d82) reduces the AC-poll interval from 20 s → 2 s, adds the new script to the shell coverage list, and tidies formatting — none of the previously reported issues have been resolved.

  • Adds systemd.user.services.ac-idle-inhibit (Type=simple, Restart=on-failure, bound to graphical-session.target) running ac-idle-inhibit.sh, which polls /sys/class/power_supply/ACAD/online every 2 s (was 20 s) and holds systemd-inhibit --what=idle ... sleep 2 while on AC
  • Sets explicit idle.{screenOffTimeout = 300, lockTimeout = 300, suspendTimeout = 600} and general.lockOnSuspend = true
  • Adds a Workspaces widget to the bar's left side and removes Settings from the right side
  • Switches ui.fontDefault from JetBrainsMono Nerd Font to Noto Sans and reformats the darkModeChange hook to multi-line, additionally writing gtk icon-theme = 'Adwaita' in both branches
  • Hides the dock indicator (showDockIndicator = false)
  • Adds config/noctalia/ac-idle-inhibit.sh to spec/coverage_spec.sh "no shell scripts are missing from coverage list" check, and tidies indentation in the script and inline-comment spacing in default.nix

Issues

5 potential issues found:

  • ac-idle-inhibit.sh hardcodes /sys/class/power_supply/ACAD/online and silently falls back to "no inhibit" when the path is missing (because of cat ... 2>/dev/null). The repo's existing AC-aware scripts (home-manager/modules/local-scripts/decafinate.sh, scripts/wallpaper-power-check.sh) use a AC*/online glob or a substituted variable for portability — the new script diverges from that pattern. Currently okay because noctalia is matic-only, but it is a footgun if the config is ever loaded on another desktop host. → Autofix
  • Bar widget id Workspaces is not registered — should be Workspace (singular). noctalia's BarWidgetRegistry.qml only registers the singular id, so this widget will silently fail to load and leave an empty slot in the bar with a "Widget not found in registry" warning. → Autofix
  • The # 5 min on battery / # 10 min on battery comments on the noctalia idle block are misleading — noctalia has no power-state awareness and these timeouts apply unconditionally. Compounded with the non-functional ac-idle-inhibit unit, future readers will assume the timers only fire on battery when in fact they fire on AC too. → Autofix
  • ac-idle-inhibit.service has Restart = "on-failure" but no RestartSec/StartLimit* settings; if systemd-inhibit ever exits non-zero (e.g., logind/polkit transiently unreachable at session start), set -e aborts the script and systemd respawns it at the default ~100 ms cadence until the start-limit trips. This is a latent issue; trigger condition is any startup race where logind isn't yet reachable from the user session. → Autofix
  • ac-idle-inhibit does not actually inhibit noctalia's idle on AC. noctalia detects idle exclusively via Wayland ext-idle-notify-v1, and Hyprland's implementation only honors Wayland surface inhibitors (zwp_idle_inhibitor_v1), not systemd-inhibit --what=idle from logind — so the script runs continuously but produces no observable behavior change, and the screen still goes off / locks at 5 min and the laptop still suspends at 10 min while plugged in. → Autofix

CI Checks

Waiting for CI checks...


⚡ Autofix All Issues

@mesa-dot-dev

mesa-dot-dev Bot commented May 4, 2026

Copy link
Copy Markdown

You do not have enough credits to review this pull request. Please purchase more credits to continue.

@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

A new Bash script checks AC power status and runs systemd-inhibit to block idle when on AC, complemented by a systemd user service and multiple noctalia configuration updates including power management settings, UI typography, dark mode hooks, dock behavior, and idle timeouts.

Changes

AC Power Idle Inhibition & Noctalia Configuration

Layer / File(s) Summary
AC Idle Inhibition Script
config/noctalia/ac-idle-inhibit.sh
Infinite loop that polls /sys/class/power_supply/ACAD/online to detect AC power and invokes systemd-inhibit to block idle for 20 seconds when on AC, otherwise sleeps for 20 seconds.
Systemd Service Integration
config/noctalia/default.nix (lines 12–26)
New systemd.user.services.ac-idle-inhibit service runs the script, restarts on failure, and is ordered as part of the graphical session target.
Power Management Configuration
config/noctalia/default.nix (lines 87–103, 111–116)
Adds lockOnSuspend = true and replaces simple idle.enabled with detailed idle attribute set (screenOffTimeout = 300, lockTimeout = 300, suspendTimeout = 600); reworkes darkModeChange hook from single-line dconf into multiline if/else script for theme/color-scheme synchronization.
UI & Behavior Configuration
config/noctalia/default.nix (lines 38, 63–67, 107–108)
Adds Workspaces widget to left bar, changes fontDefault to "Noto Sans", sets showDockIndicator = false.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

A rabbit hops through power states with glee,
Blocks idle when the wall feeds AC,
New dark themes bloom, the fonts refresh,
And dock indicators turn to mesh,
One cozy config, now complete ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: lid lock on suspend, AC-aware idle inhibition, and battery-focused timeout configuration (5min screen/lock, 10min suspend).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description clearly outlines all major changes: lid lock via lockOnSuspend, AC-aware idle inhibition, battery-based idle timeouts, and script extraction. It directly corresponds to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch update-noctalia

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mesa-dot-dev

mesa-dot-dev Bot commented May 4, 2026

Copy link
Copy Markdown

Mesa Description

TL;DR

Lock the session on lid close and make idle power-aware. On AC, idle is inhibited; on battery, the display turns off and locks at 5 minutes and the system suspends at 10 minutes.

What changed?

  • config/noctalia/ac-idle-inhibit.sh: A new script that continuously monitors AC power status and uses systemd-inhibit to prevent the system from idling when on AC.
  • config/noctalia/default.nix: Updated to enable lockOnSuspend, introduce the ac-idle-inhibit systemd user service, modify panel widgets, change the default font to Noto Sans, update the darkModeChange hook for icon themes, hide the dock indicator, and define idle timeouts for battery operation.

Description generated by Mesa. Update settings

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@config/noctalia/ac-idle-inhibit.sh`:
- Around line 6-11: The loop in ac-idle-inhibit.sh refreshes systemd-inhibit
only every 20s, causing a stale inhibitor around idle deadlines; replace the
fixed sleep-based polling with an event-driven wait on the AC sysfs file (the
"$AC" variable) or a shorter reactive mechanism so the inhibitor is
created/removed immediately when AC state changes: stop relying on the 20s sleep
in the while-true loop, watch "$AC" for changes (e.g., inotify or udev/dbus) and
run or kill the systemd-inhibit invocation accordingly (reference the while loop
and the systemd-inhibit call in the script).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: db98353d-87f8-4139-a76e-f818e3459666

📥 Commits

Reviewing files that changed from the base of the PR and between 5f8ccb7 and 2f72482.

📒 Files selected for processing (2)
  • config/noctalia/ac-idle-inhibit.sh
  • config/noctalia/default.nix

Comment thread config/noctalia/ac-idle-inhibit.sh Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a mechanism to inhibit system idle when connected to AC power through a new bash script and a corresponding systemd user service. Additionally, it updates the desktop environment configuration, including UI adjustments (widgets, fonts, and dock indicators), security enhancements like locking on suspend, and specific idle timeouts for battery usage. Feedback focuses on improving the robustness of the AC detection script by avoiding hardcoded hardware paths, ensuring the systemd service explicitly includes necessary binaries in its environment, and optimizing the dark mode hook for better maintainability.

Comment on lines +4 to +12
AC=/sys/class/power_supply/ACAD/online

while true; do
if [ "$(cat "$AC" 2>/dev/null)" = "1" ]; then
systemd-inhibit --what=idle --why="On AC power" --mode=block sleep 20
else
sleep 20
fi
done

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The script hardcodes the AC adapter path to /sys/class/power_supply/ACAD/online, which is hardware-dependent and may not exist on all systems (e.g., some use AC or ADP1). Using a glob with grep is more robust and avoids potential failures if the specific path is missing. Additionally, since set -e is used, the script would terminate if the hardcoded path was missing and accessed via a subshell in a different context.

Suggested change
AC=/sys/class/power_supply/ACAD/online
while true; do
if [ "$(cat "$AC" 2>/dev/null)" = "1" ]; then
systemd-inhibit --what=idle --why="On AC power" --mode=block sleep 20
else
sleep 20
fi
done
while true;
do
if grep -q 1 /sys/class/power_supply/*/online 2>/dev/null; then
systemd-inhibit --what=idle --why="On AC power" --mode=block sleep 20
else
sleep 20
fi
done

Comment on lines +19 to +23
Service = {
Type = "simple";
ExecStart = "${pkgs.bash}/bin/bash ${./ac-idle-inhibit.sh}";
Restart = "on-failure";
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The systemd service relies on systemd-inhibit, grep, and sleep being in the PATH. In a systemd user service, the environment might be restricted. It is safer to explicitly provide the required packages in the service's path attribute to ensure reliability across different environments.

    path = [ pkgs.systemd pkgs.coreutils pkgs.gnugrep ];
    Service = {
      Type = "simple";
      ExecStart = "${pkgs.bash}/bin/bash ${./ac-idle-inhibit.sh}";
      Restart = "on-failure";
    };

Comment on lines +92 to +102
darkModeChange = ''
if [ "$1" = "true" ]; then
dconf write /org/gnome/desktop/interface/color-scheme "'prefer-dark'"
dconf write /org/gnome/desktop/interface/gtk-theme "'Adwaita-dark'"
dconf write /org/gnome/desktop/interface/icon-theme "'Adwaita'"
else
dconf write /org/gnome/desktop/interface/color-scheme "'prefer-light'"
dconf write /org/gnome/desktop/interface/gtk-theme "'Adwaita'"
dconf write /org/gnome/desktop/interface/icon-theme "'Adwaita'"
fi
'';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The icon-theme is set to the same value ('Adwaita') in both branches of the if statement. Moving it outside the conditional block improves maintainability. Additionally, using the absolute path to the dconf binary from pkgs makes the hook more robust against environment variations, adhering to the repository's pattern for Nix-extracted scripts.

        darkModeChange = ''
          ${pkgs.dconf}/bin/dconf write /org/gnome/desktop/interface/icon-theme "'Adwaita'"
          if [ "$1" = "true" ]; then
            ${pkgs.dconf}/bin/dconf write /org/gnome/desktop/interface/color-scheme "'prefer-dark'"
            ${pkgs.dconf}/bin/dconf write /org/gnome/desktop/interface/gtk-theme "'Adwaita-dark'"
          else
            ${pkgs.dconf}/bin/dconf write /org/gnome/desktop/interface/color-scheme "'prefer-light'"
            ${pkgs.dconf}/bin/dconf write /org/gnome/desktop/interface/gtk-theme "'Adwaita'"
          fi
        '';
References
  1. Maintain consistency with established patterns for writing scripts that are extracted from Nix expressions.

capsuleOpacity = 0;
widgets.left = [
{ id = "Launcher"; }
{ id = "Workspaces"; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid widget id — Workspaces (plural) is not registered in noctalia.

noctalia-shell @ 9f8dd48 only registers the singular Workspace in Services/UI/BarWidgetRegistry.qml, and BarWidgetLoader.qml loads widgets via _barWidgetsDir + widgetId + ".qml" against Modules/Bar/Widgets/Workspace.qml. With Workspaces (plural), BarWidgetRegistry.hasWidget() returns false, the loader logs "Widget not found in registry", and the slot renders nothing — so the workspaces indicator never appears between Launcher and Clock.

Fix: use the singular id.

Suggested change
{ id = "Workspaces"; }
{ id = "Workspace"; }

Comment thread config/noctalia/ac-idle-inhibit.sh Outdated

while true; do
if [ "$(cat "$AC" 2>/dev/null)" = "1" ]; then
systemd-inhibit --what=idle --why="On AC power" --mode=block sleep 20

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This systemd-inhibit --what=idle has no effect on noctalia or Hyprland.

noctalia-shell @ 9f8dd48 detects idle solely through Quickshell.Wayland.IdleMonitorext-idle-notify-v1 (Services/Power/IdleService.qml); a repo-wide grep for login1/logind/ListInhibitors in noctalia returns zero hits, and Services/Power/IdleInhibitorService.qml only emits outgoing inhibitors — it does not listen for external ones.

Hyprland is the same story: src/protocols/IdleNotify.cpp::CExtIdleNotification::update gates only on PROTO::idle->isInhibited, and src/managers/input/IdleInhibitor.cpp::CInputManager::recheckIdleInhibitorStatus only flips that flag for live zwp_idle_inhibit_manager_v1 surfaces or Hyprland idleinhibit window rules. There is no logind/sd-bus integration anywhere in the Hyprland tree.

Net effect with idle = { screenOffTimeout = 300; lockTimeout = 300; suspendTimeout = 600; }: the screen still goes off and locks at 5 min and the machine still suspends at 10 min while on AC. The unit + script run continuously but change nothing observable.

To actually keep the machine awake on AC, hold a Wayland surface inhibitor (e.g. wayland-idle-inhibitor/wlinhibit) or set a Hyprland idleinhibit always window rule — those are the only inputs to recheckIdleInhibitorStatus().

Service = {
Type = "simple";
ExecStart = "${pkgs.bash}/bin/bash ${./ac-idle-inhibit.sh}";
Restart = "on-failure";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add RestartSec to avoid a tight restart loop on transient failures.

The script runs under set -euo pipefail, so any non-zero exit from systemd-inhibit (e.g., logind not yet reachable early in the graphical session, polkit denial, missing binary on PATH) aborts the loop. With Restart = "on-failure" and no RestartSec, systemd will respawn at the default ~100 ms until the default start-limit trips and the unit is marked failed silently.

Other similar units in this repo set this:

  • home-manager/services/keyd-application-mapper/default.nix:40RestartSec = 3;
  • named-hosts/matic/default.nix:462 (wallpaper-power-monitor) — RestartSec = 5;

Suggested:

      Restart = "on-failure";
      RestartSec = 5;

Comment thread config/noctalia/default.nix Outdated
idle.enabled = true;
idle = {
enabled = true;
screenOffTimeout = 300; # 5 min on battery

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment is misleading — these timeouts apply on AC too.

noctalia has no power-state awareness: Services/Power/IdleService.qml binds screenOffTimeout/lockTimeout/suspendTimeout directly to Quickshell.Wayland.IdleMonitor.timeout for all stages and never branches on AC vs battery. The # 5 min on battery / # 10 min on battery annotations imply per-power-state behavior that does not exist (and the ac-idle-inhibit unit does not change this — see the inline comment on ac-idle-inhibit.sh).

Drop the on battery qualifier or remove the comments.

#!/usr/bin/env bash
set -euo pipefail

AC=/sys/class/power_supply/ACAD/online

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded ACAD path diverges from the repo's portable pattern.

This repo already has two AC-aware scripts that don't hardcode the adapter name:

  • home-manager/modules/local-scripts/decafinate.sh:42-46 and :174-178 iterate for ac_path in /sys/class/power_supply/AC*/online; do ....
  • scripts/wallpaper-power-check.sh takes the path as @ac_supply_path@ via pkgs.replaceVars.

With the hardcoded path here, cat "$AC" 2>/dev/null returns empty when the file is missing, the test fails, and the script silently behaves as if on battery — with no warning. It works today only because config/noctalia/ is loaded under isDesktop = true, which only named-hosts/matic enables. If noctalia is ever turned on for another desktop host with a different adapter name (e.g. AC0, ADP1), this script will silently do nothing and the failure will be invisible.

Consider switching to the AC*/online glob to match the existing pattern.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="spec/coverage_spec.sh">

<violation number="1" location="spec/coverage_spec.sh:363">
P2: This new shell script is added to the coverage list without a corresponding spec-file check, so the coverage guard no longer verifies test-file presence for it.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread spec/coverage_spec.sh
config/hyprland/scripts/record-screen.sh
config/hyprland/scripts/toggle-terminal.sh
config/k3s/activate.sh
config/noctalia/ac-idle-inhibit.sh

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This new shell script is added to the coverage list without a corresponding spec-file check, so the coverage guard no longer verifies test-file presence for it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At spec/coverage_spec.sh, line 363:

<comment>This new shell script is added to the coverage list without a corresponding spec-file check, so the coverage guard no longer verifies test-file presence for it.</comment>

<file context>
@@ -360,6 +360,7 @@ config/git-ai/activate.sh
 config/hyprland/scripts/record-screen.sh
 config/hyprland/scripts/toggle-terminal.sh
 config/k3s/activate.sh
+config/noctalia/ac-idle-inhibit.sh
 config/obsidian/activate.sh
 config/omp/activate.sh
</file context>

@shunkakinoki
shunkakinoki merged commit 4391c90 into main May 4, 2026
18 checks passed
@shunkakinoki
shunkakinoki deleted the update-noctalia branch May 4, 2026 13:43
@indent-zero indent-zero Bot mentioned this pull request May 4, 2026
1 task
shunkakinoki added a commit that referenced this pull request May 5, 2026
…1651)

* chore: update noctalia

* feat: lock screen on lid close

* feat: ac-aware idle - inhibit on AC, 5min dimlock 10min suspend on battery

* fix: extract ac-idle-inhibit to external script for inline check

* fix: reduce AC poll to 2s, add coverage entry, fix nix formatting
@coderabbitai coderabbitai Bot mentioned this pull request May 12, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant