← All solutions
Caelestia Shell · Notifications

Implementing Persistent
Notification History

Caelestia Shell's default notification dock only keeps track of active, undismissed notifications. Once closed, they are discarded. Here is how I modified the notification engine to implement a persistent notification log, following the same architectural design I used for the Clipboard manager.

Caelestia Shell Quickshell / QML Notifs Service
Current Limitation

By default, Caelestia's Notifs.qml service handles incoming notifications using a NotificationServer. It holds a list of active alerts:

qml
property list<NotifData> list: []
readonly property list<NotifData> notClosed: list.filter(n => !n.closed)

When a notification is dismissed (either by clicking the close button or clearing all), the NotifData object gets removed from the memory list, and the cache is updated. There is no historical log to review missed alerts.

How to Build it (Step-by-Step)

To implement a notification history/log, I mirrored the design of the clipboard service by adding cache serialization and separate data models.

Extend the Notifs Service

In services/Notifs.qml, I add a separate cache file (~/.cache/caelestia/notif_history.json) to hold dismissed notifications and intercept the deletion event:

qml
// Inside Notifs.qml
property list<var> historyList: []

function addToHistory(notif) {
    let serialized = {
        title: notif.summary,
        body: notif.body,
        appName: notif.appName,
        time: new Date().toLocaleTimeString(),
        icon: notif.appIcon
    };

    // Add to top and cap list at 50 items
    historyList.unshift(serialized);
    if (historyList.length > 50) historyList.pop();

    // Persist to json cache
    saveHistoryToFile();
}

Hook the Dismiss Event

In services/NotifData.qml, right before the notification is removed from the active lists, I invoke the new service function:

qml
// Inside NotifData.qml (close/destruction logic)
onClosedChanged: {
    if (closed) {
        Notifs.addToHistory(this);
    }
}

Create the History UI

I create a new NotifHistoryView.qml under modules/sidebar/. It uses a ListView displaying the Notifs.historyList array, allowing scrolling through past alerts, clearing individual items, or flushing the entire JSON log.

Why this Pattern Works

By separating active/floating notifications from historical serialized logs:

  • The active notification UI stays extremely fast and responsive.
  • Dismissed notifications don't waste system resources or memory inside in-flight QML objects.
  • Data is written asynchronously to a local JSON cache, making it lightweight and persistent across reboots.