Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
d5f64f6
Keep active history limit independent
locainin Jul 5, 2026
6de6a1f
Preserve inline notification markup adjacency
locainin Jul 5, 2026
8229069
Bound notification image metadata
locainin Jul 5, 2026
d1aaded
Expose split clear commands on control proxy
locainin Jul 5, 2026
3c050a3
Split noticenterctl CLI parsing modules
locainin Jul 5, 2026
2131e36
Split noticenterctl D-Bus dispatch
locainin Jul 5, 2026
2422c56
Clean noticenterctl comments
locainin Jul 5, 2026
a990ee1
Move daemon control server into folder
locainin Jul 5, 2026
7e667d7
Organize daemon notification server
locainin Jul 5, 2026
dd796f6
Split daemon state into focused modules
locainin Jul 5, 2026
a75ea0d
Make daemon root module paths explicit
locainin Jul 5, 2026
f935b19
Use an unbounded expiration scheduler channel
locainin Jul 5, 2026
7153e4b
Test scheduled notification expiration
locainin Jul 5, 2026
99c4617
Keep active notifications when history is disabled
locainin Jul 5, 2026
0cb9b37
Clean daemon comments after refactor
locainin Jul 5, 2026
3c79e73
Add installer shell PATH cleanup
locainin Jul 5, 2026
33f5717
Stop daemon before uninstall cleanup
locainin Jul 5, 2026
9712293
Add installer version flag
locainin Jul 5, 2026
5dcbafc
Validate release archive binaries from manifest
locainin Jul 5, 2026
b17d07f
Package release binaries from workspace metadata
locainin Jul 5, 2026
cf8d520
refactor: move notification tooling into domain modules
locainin Jul 5, 2026
6e7d8e7
noticenterctl: tighten css cache and theme modules
locainin Jul 5, 2026
b1482c7
noticenterctl: expand css-check parser and lint coverage
locainin Jul 5, 2026
eab615c
noticenterctl: cover css geometry feature folders
locainin Jul 5, 2026
922cb82
noticenterctl: organize dbus commands and output
locainin Jul 5, 2026
38cd82d
noticenterctl: split debug log follower
locainin Jul 5, 2026
51396c3
noticenterctl: add preset core coverage
locainin Jul 5, 2026
1c86fdb
noticenterctl: harden preset import and export tests
locainin Jul 5, 2026
4625a08
noticenterctl: add binary cli integration tests
locainin Jul 5, 2026
0e88d35
daemon: split store root implementation
locainin Jul 5, 2026
69ad530
noticenterctl: remove preset path redirects
locainin Jul 5, 2026
cf4d31e
noticenterctl: align integration tests with CI clippy
locainin Jul 5, 2026
1f102ca
refactor(noticenterctl): rename CLI command module
locainin Jul 6, 2026
2c957b9
refactor(noticenterctl): isolate local command dispatch
locainin Jul 6, 2026
c24b775
test(noticenterctl): cover D-Bus timeout helper
locainin Jul 6, 2026
62757c2
test(noticenterctl): cover debug panel log following
locainin Jul 6, 2026
19fb521
refactor(noticenterctl): split preset import modules
locainin Jul 6, 2026
9a80c74
test(noticenterctl): expand preset import core coverage
locainin Jul 6, 2026
29ad008
test(noticenterctl): cover preset import commit rollback
locainin Jul 6, 2026
f01ca34
test(noticenterctl): cover preset import prompts and summaries
locainin Jul 6, 2026
e170616
refactor(noticenterctl): move exec review tests under import tests
locainin Jul 6, 2026
070daf6
feat(center): apply configured panel action order
locainin Jul 6, 2026
3ea73e8
fix(center): keep panel close placement consistent
locainin Jul 6, 2026
bb2ae84
fix(center): preserve panel section order on reload
locainin Jul 6, 2026
ef75a53
test(center): cover panel section ordering
locainin Jul 6, 2026
f5e9d2e
refactor(core): move panel config into panel module
locainin Jul 6, 2026
846bb6a
test(core): add panel config module coverage
locainin Jul 6, 2026
dc89d74
test(core): move media config tests beside media module
locainin Jul 6, 2026
c390cb4
test(core): move runtime sanitize tests beside sanitizer
locainin Jul 6, 2026
fe5b9da
test(core): move runtime widget backend tests
locainin Jul 6, 2026
31dcc81
test(core): split widget root tests into folder
locainin Jul 6, 2026
5f461c8
test(core): add slider and toggle config coverage
locainin Jul 6, 2026
fed6f69
test(core): add card stat and plugin config coverage
locainin Jul 6, 2026
15a8c1c
test(noticenterctl): cover debug log command behavior
locainin Jul 6, 2026
3c3c14e
test(noticenterctl): cover preset import failure policy
locainin Jul 6, 2026
a537fd1
test(daemon): expose expiration scheduler test channel
locainin Jul 6, 2026
f4626a6
test(daemon): cover state scheduler and notification paths
locainin Jul 6, 2026
1200331
test(daemon): cover control server state changes
locainin Jul 6, 2026
1e25e9e
test(daemon): cover notification server wrappers
locainin Jul 6, 2026
cae2e35
test(daemon): cover runtime and entrypoint edges
locainin Jul 6, 2026
3052d84
test(installer): cover path cleanup and binary help
locainin Jul 6, 2026
4730fb3
test(installer): make stdout probe tests mutation-stable
locainin Jul 6, 2026
123fdf5
style: normalize inline comment punctuation
locainin Jul 6, 2026
43d8323
fix(release): package runtime binaries from metadata
locainin Jul 6, 2026
9383923
chore(release): prepare v1.1.0 metadata
locainin Jul 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
workflow_dispatch:
inputs:
tag:
description: Release tag to package, such as v1.0.0
description: Release tag to package, such as v1.1.0
required: true
type: string

Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ If docs live in the Wiki and are not updated in the PR, note what needs to be up

## Releases

UnixNotis releases are cut from `master` with tags such as `v1.0.0`.
UnixNotis releases are cut from `master` with tags such as `v1.1.0`.

Before tagging a release:

Expand All @@ -192,7 +192,7 @@ Before tagging a release:
Build the release archive manually before creating the GitHub release:

```sh
scripts/package-release.sh v1.0.0
scripts/package-release.sh v1.1.0
```

The same packaging step can be run from the manual **Release Package** GitHub Actions workflow. It
Expand Down
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ members = [
resolver = "2"

[workspace.package]
version = "1.0.0"
version = "1.1.0"
edition = "2021"
license = "MIT"

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ reports when a newer GitHub release is available.
Maintainers can build a local release archive manually:

```sh
scripts/package-release.sh v1.0.0
scripts/package-release.sh v1.1.0
```

## Development
Expand Down
95 changes: 95 additions & 0 deletions crates/noticenterctl/src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//! Application runner for noticenterctl

use anyhow::{Context, Result};
use clap::Parser;
use unixnotis_core::ControlProxy;
use zbus::Connection;

use crate::cli::{Args, Command};

pub(crate) async fn run() -> Result<()> {
// Parse CLI arguments before any daemon work starts
let args = Args::parse();
let command = args.command;

if command.is_local_only() {
// Local commands must work even when the daemon is not running
handle_local_command(command, crate::css_check::run, crate::preset::run_preset)?;
return Ok(());
}

// Control commands need the session bus and the daemon proxy
let connection = Connection::session()
.await
.context("connect to session bus")?;
let proxy = ControlProxy::new(&connection)
.await
.context("connect to unixnotis control interface")?;

crate::dbus::handle_command(&proxy, command).await
}

fn handle_local_command(
command: Command,
mut run_css: impl FnMut() -> Result<()>,
mut run_preset: impl FnMut(crate::cli::PresetCommand) -> Result<()>,
) -> Result<()> {
match command {
Command::CssCheck => run_css(),
Command::Preset { command } => run_preset(command).context("preset command failed"),
_ => Ok(()),
}
}

#[cfg(test)]
mod tests {
use std::cell::Cell;

use anyhow::Result;

use crate::cli::{Command, PresetCommand};

use super::handle_local_command;

#[test]
fn handle_local_command_runs_css_check_branch() {
let css_called = Cell::new(false);

handle_local_command(
Command::CssCheck,
|| {
css_called.set(true);
Ok(())
},
|_| -> Result<()> { panic!("preset runner should not be called for css check") },
)
.expect("css check should dispatch");

assert!(css_called.get());
}

#[test]
fn handle_local_command_runs_preset_branch_with_command_payload() {
let preset_called = Cell::new(false);

handle_local_command(
Command::Preset {
command: PresetCommand::Inspect {
input: "theme.unixnotis".to_string(),
},
},
|| -> Result<()> { panic!("css runner should not be called for preset command") },
|command| {
let PresetCommand::Inspect { input } = command else {
panic!("expected inspect preset command");
};
assert_eq!(input, "theme.unixnotis");
preset_called.set(true);
Ok(())
},
)
.expect("preset should dispatch");

assert!(preset_called.get());
}
}
89 changes: 89 additions & 0 deletions crates/noticenterctl/src/cli/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use clap::{Parser, Subcommand, ValueEnum};
use unixnotis_core::{PanelDebugLevel, INHIBIT_SCOPE_ALL, INHIBIT_SCOPE_POPUPS};

use super::command::Command;

#[derive(Parser, Debug)]
#[command(author, version, about)]
pub(crate) struct Args {
// Subcommands map 1:1 to the daemon control surface
#[command(subcommand)]
pub(crate) command: Command,
}

#[derive(ValueEnum, Debug, Clone, Copy)]
pub(crate) enum DebugLevelArg {
// Only critical diagnostic output
Critical,
// Warnings and above
Warn,
// Informational output
Info,
// Verbose diagnostics
Verbose,
}

impl From<DebugLevelArg> for PanelDebugLevel {
fn from(value: DebugLevelArg) -> Self {
match value {
DebugLevelArg::Critical => PanelDebugLevel::Critical,
DebugLevelArg::Warn => PanelDebugLevel::Warn,
DebugLevelArg::Info => PanelDebugLevel::Info,
DebugLevelArg::Verbose => PanelDebugLevel::Verbose,
}
}
}

#[derive(ValueEnum, Debug, Clone, Copy)]
pub(crate) enum DndState {
// Explicitly enable DND
On,
// Explicitly disable DND
Off,
// Toggle based on current daemon state
Toggle,
}

#[derive(ValueEnum, Debug, Clone, Copy)]
pub(crate) enum InhibitScopeArg {
// Suppress both panel and popup updates
All,
// Suppress popup updates only
Popups,
}

impl InhibitScopeArg {
pub(crate) fn as_scope(self) -> u32 {
// Map CLI scope to the daemon bitmask value
match self {
Self::All => INHIBIT_SCOPE_ALL,
Self::Popups => INHIBIT_SCOPE_POPUPS,
}
}
}

#[derive(Subcommand, Debug)]
pub(crate) enum PresetCommand {
// Export the current config tree into one shareable bundle file
Export {
output: String,
#[arg(long = "except", value_name = "PATH")]
except: Vec<String>,
#[arg(long)]
force: bool,
},
// Import a bundle into the current config tree
Import {
input: String,
#[arg(long = "except", value_name = "PATH")]
except: Vec<String>,
#[arg(long)]
dry_run: bool,
#[arg(long)]
allow_exec: bool,
},
// Print bundle metadata and included files without writing anything
Inspect {
input: String,
},
}
69 changes: 69 additions & 0 deletions crates/noticenterctl/src/cli/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use clap::Subcommand;

use super::args::{DebugLevelArg, DndState, InhibitScopeArg, PresetCommand};

#[derive(Subcommand, Debug)]
pub(crate) enum Command {
// Toggle the panel visibility without changing other state
TogglePanel,
// Open the panel, optionally enabling debug logging for live diagnostics
OpenPanel {
#[arg(long, value_enum, num_args = 0..=1, default_missing_value = "info")]
debug: Option<DebugLevelArg>,
},
// Close the panel if it is visible
ClosePanel,
// Set or toggle Do Not Disturb mode
Dnd {
#[arg(value_enum)]
state: DndState,
},
// Clear active notifications and saved history
Clear,
// Clear active notifications and saved history
ClearAll,
// Clear active notifications without deleting saved history
ClearActive,
// Clear saved history without closing active notifications
ClearHistory,
// Dismiss a single notification by identifier
Dismiss {
id: u32,
},
// List active notifications; full output requires diagnostic mode
ListActive {
#[arg(long)]
full: bool,
},
// List notification history; full output requires diagnostic mode
ListHistory {
#[arg(long)]
full: bool,
},
// Create a new inhibitor token
Inhibit {
reason: String,
#[arg(long, value_enum, default_value = "all")]
scope: InhibitScopeArg,
},
// Remove an inhibitor by token
Uninhibit {
id: u64,
},
// Print current inhibitors to stdout
ListInhibitors,
// Validate theme CSS files without touching D-Bus
CssCheck,
// Export, inspect, or import a shareable preset bundle
Preset {
#[command(subcommand)]
command: PresetCommand,
},
}

impl Command {
pub(crate) fn is_local_only(&self) -> bool {
// Local-only commands should not fail just because D-Bus is unavailable
matches!(self, Command::CssCheck | Command::Preset { .. })
}
}
12 changes: 12 additions & 0 deletions crates/noticenterctl/src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! Command-line argument types for noticenterctl

mod args;
mod command;

pub(crate) use args::{Args, DndState, PresetCommand};
#[cfg(test)]
pub(crate) use args::{DebugLevelArg, InhibitScopeArg};
pub(crate) use command::Command;

#[cfg(test)]
mod tests;
Loading
Loading