Skip to content

Add background desktop notifications - #1055

Merged
alexeyzimarev merged 5 commits into
mainfrom
feat/1047-desktop-notifications
Sep 20, 2026
Merged

alexeyzimarev merged 5 commits into
mainfrom
feat/1047-desktop-notifications

Conversation

@alexeyzimarev

@alexeyzimarev alexeyzimarev commented Sep 19, 2026 •

Copy link
Copy Markdown
Member

Closes #1047 — AI-2997

What & why

Notify users when an agent needs permission, asks a question, or becomes idle while Capacitor is in the background. Notifications offer supported permission actions or open the agent; three settings switches control each category independently.

Where to look

The coordinator deduplicates local/server requests, survives reconnects, and validates live requests before answering. Remote subscriptions stay active with the window hidden; remote idle is not inferred.

Verification

  • dotnet run desktop unit project: 2,636 passed with en_US.UTF-8; affected daemon suite: 62 passed; IPC suite: 27 passed.
  • Release app rebuild: 0 warnings/errors. macOS app and CLI/daemon AOT publishes passed; local AOT used AppleMinOSVersion=26.0 for Homebrew libraries.
  • Signed isolated macOS smoke verified authorization, background delivery, withdrawal, and disposal. OS button/body clicks and foreground presentation remain unverified; action routing and foreground suppression have unit coverage.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-19T14:36:26.495013Z ae2df69 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add actionable background desktop notifications

✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Notify background users about permission requests, questions, and completed agent turns.
• Route supported notification actions through validated live permission requests or agent
 navigation.
• Add independent persisted settings and reconnect-safe subscriptions for each notification
 category.
Diagram

graph TD
  UI["Settings UI"] -->|"save categories"| Settings["Notification settings"] -->|"live preferences"| Coordinator["Notification coordinator"] -->|"show or withdraw"| Sink["Native sink"] -->|"deliver"| OS["Desktop OS"]
  Feeds["Agent permission feeds"] -->|"requests and states"| Coordinator
  OS -->|"user action"| Sink
  Sink -->|"activation callback"| Coordinator
  Coordinator -->|"resolve or open"| Actions["Permission navigation"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use Avalonia Labs on every platform
  • ➕ Reduces custom native interop code
  • ➕ Provides one notification abstraction across platforms
  • ➖ The macOS backend has unsafe asynchronous authorization failure behavior
  • ➖ It cannot reliably withdraw delivered macOS notifications
  • ➖ It does not satisfy the bundled-app lifecycle requirements
2. Bind notifications to visible workspaces
  • ➕ Reuses existing workspace subscriptions
  • ➕ Requires less app-lifetime coordination
  • ➖ Hidden windows would release remote request subscriptions
  • ➖ Reconnects and local/server handovers could lose or duplicate alerts
  • ➖ Notification lifecycle would become coupled to UI visibility
3. Open the app for every notification action
  • ➕ Avoids native permission decision routing
  • ➕ Reduces stale-action security risk
  • ➖ Removes actionable Allow, Always, and Decline controls
  • ➖ Adds friction for common permission requests
  • ➖ Does not meet the requested background workflow

Recommendation: Keep the PR's centralized app-lifetime coordinator and hybrid native adapter. Direct macOS UserNotifications addresses concrete lifecycle and withdrawal gaps in the shared library, while Avalonia Labs remains a pragmatic optional backend elsewhere; validating every action against the live permission handle appropriately contains stale or duplicated requests.

Files changed (38) +2539 / -90

Enhancement (16) +1053 / -71
App.axamlStyle settings tabs and notification switches +68/-0

Style settings tabs and notification switches

• Adds Kcap-themed tab and toggle-switch styles, including hover, selected, checked, disabled, and keyboard-focus states.

src/Capacitor.App/App.axaml

App.axaml.csWire notifications into the application lifetime +33/-3

Wire notifications into the application lifetime

• Creates shared notification settings, native sink, coordinator, and remote session subscriptions in the daemon graph. Passes settings into the settings window and disposes notification resources during failures and shutdown.

src/Capacitor.App/App.axaml.cs

NotificationPreferences.csDefine notification category preferences +6/-0

Define notification category preferences

• Introduces an immutable preference model whose permission, question, and idle categories default to enabled.

src/Capacitor.App/Services/NotificationPreferences.cs

NotificationSettingsService.csPersist and publish notification preferences +92/-0

Persist and publish notification preferences

• Loads tolerant defaults, publishes immediate preference changes, and atomically serializes settings to notifications.json. Concurrent saves and disposal are coordinated so the latest accepted state is retained.

src/Capacitor.App/Services/NotificationSettingsService.cs

DesktopNotification.csDefine the platform-neutral notification model +3/-0

Define the platform-neutral notification model

• Adds a notification record containing identity, title, body, and supported actions.

src/Capacitor.App/Services/Notifications/DesktopNotification.cs

DesktopNotificationAction.csDefine notification action metadata +3/-0

Define notification action metadata

• Adds the platform-neutral action identifier and display-label record.

src/Capacitor.App/Services/Notifications/DesktopNotificationAction.cs

DesktopNotificationCoordinator.csCoordinate actionable background notifications +253/-0

Coordinate actionable background notifications

• Observes permission requests, agent state, connection state, and preferences to create and withdraw deduplicated notifications. It preserves valid alerts across handovers, detects local idle transitions, validates live request handles before responding, and routes failures or body clicks into the app.

src/Capacitor.App/Services/Notifications/DesktopNotificationCoordinator.cs

IDesktopNotificationSink.csAbstract desktop notification delivery +6/-0

Abstract desktop notification delivery

• Defines show, close, activation callback, and disposal operations for native notification adapters.

src/Capacitor.App/Services/Notifications/IDesktopNotificationSink.cs

MacOsDesktopNotificationSink.csDeliver actionable notifications through macOS UserNotifications +192/-0

Deliver actionable notifications through macOS UserNotifications

• Adds bundled-app detection, authorization, dynamic action categories, delivery, foreground suppression, callbacks, and complete withdrawal of pending and delivered notifications. Native callbacks are marshalled to the UI thread and stale identities are rejected.

src/Capacitor.App/Services/Notifications/MacOsDesktopNotificationSink.cs

NativeDesktopNotificationSink.csSelect and guard platform notification backends +155/-0

Select and guard platform notification backends

• Uses direct UserNotifications on macOS and Avalonia Labs on optional Windows/Linux paths. It escapes Windows XML, assigns unguessable action tags, contains asynchronous backend failures, and rejects stale or dismissed activations.

src/Capacitor.App/Services/Notifications/NativeDesktopNotificationSink.cs

NotificationSessionSubscriptions.csKeep remote notification sessions subscribed +27/-0

Keep remote notification sessions subscribed

• Acquires one app-lifetime session lease per current remote session and releases leases when sessions disappear or notification infrastructure shuts down.

src/Capacitor.App/Services/Notifications/NotificationSessionSubscriptions.cs

PendingPermissionRequest.csTrack permission action capabilities and subscription loss +8/-0

Track permission action capabilities and subscription loss

• Derives Allow and Always capabilities from explicit IPC metadata with legacy vendor fallbacks. Adds a subscription-loss marker so reconnect removals can be distinguished from settled requests.

src/Capacitor.App/Services/PendingPermissionRequest.cs

SettingsViewModel.csExpose live notification settings +69/-1

Expose live notification settings

• Adds independently bound permission, question, and idle switches backed by the shared settings service. Changes apply immediately, persist asynchronously, and surface save failures without reverting the runtime preference.

src/Capacitor.App/ViewModels/SettingsViewModel.cs

SettingsWindow.axamlAdd a Notifications settings tab +134/-65

Add a Notifications settings tab

• Reorganizes daemon settings into a tab and adds a second tab with accessible switches for all three notification categories. The existing fixed window size and daemon controls are preserved.

src/Capacitor.App/Views/SettingsWindow.axaml

PermissionIpc.csAdvertise optional permission grant capabilities +2/-1

Advertise optional permission grant capabilities

• Extends pending-permission IPC payloads with backward-compatible nullable Allow Once and Allow Always capability flags.

src/Capacitor.Cli.Core/LocalIpc/PermissionIpc.cs

LocalPermissionBridge.csPublish hook-vendor grant capabilities +2/-1

Publish hook-vendor grant capabilities

• Marks Claude and Codex hook requests as supporting one-time grants and limits standing grants to Claude.

src/Capacitor.Cli.Daemon/Services/LocalPermissionBridge.cs

Bug fix (2) +32 / -15
PermissionService.csPreserve permission requests across reconnect handovers +21/-9

Preserve permission requests across reconnect handovers

• Sends a boolean standing-grant marker for non-Claude requests and atomically swaps disconnected local handles for server twins. Removed local requests are marked as subscription losses before observers receive the change.

src/Capacitor.App/Services/PermissionService.cs

AcpPermissionSurface.csDerive ACP grant capabilities from offered options +11/-6

Derive ACP grant capabilities from offered options

• Publishes only grant scopes represented by ACP options and treats exactly one standing grant as actionable. Settlement mapping preserves explicit standing-grant intent instead of conflating it with a one-time grant.

src/Capacitor.Cli.Daemon/Services/AcpPermissionSurface.cs

Tests (11) +1186 / -2
DesktopNotificationCoordinatorTests.csCover notification coordination and action safety +436/-0

Cover notification coordination and action safety

• Tests category and foreground filtering, supported actions, live-handle validation, deduplication, reconnects, lane handovers, foreign servers, idle edges, shutdown, and transport failures.

test/Capacitor.App.Tests.Unit/DesktopNotificationCoordinatorTests.cs

MacNotificationBlockTests.csVerify native block ownership and containment +48/-0

Verify native block ownership and containment

• Tests that copied Objective-C blocks retain managed callbacks after owner disposal and contain exceptions while preserving native error pointers.

test/Capacitor.App.Tests.Unit/MacNotificationBlockTests.cs

NativeDesktopNotificationSinkTests.csExercise cross-platform notification adapters +181/-0

Exercise cross-platform notification adapters

• Covers Windows XML escaping, Linux action routing, closure and disposal, stale tags, body clicks, dismissals, callback exceptions, and asynchronous platform failures.

test/Capacitor.App.Tests.Unit/NativeDesktopNotificationSinkTests.cs

NotificationSessionSubscriptionsTests.csVerify remote session lease lifecycle +29/-0

Verify remote session lease lifecycle

• Ensures leases are acquired once for current sessions and released on removal or disposal.

test/Capacitor.App.Tests.Unit/NotificationSessionSubscriptionsTests.cs

NotificationSettingsServiceTests.csTest notification preference persistence +101/-0

Test notification preference persistence

• Covers enabled defaults, partial and corrupt files, atomic persistence, live behavior after write failures, overlapping saves, and disposal during active writes.

test/Capacitor.App.Tests.Unit/NotificationSettingsServiceTests.cs

PermissionServiceTests.csTest capability metadata and reconnect handovers +60/-0

Test capability metadata and reconnect handovers

• Adds coverage for vendor and explicit grant capabilities, ACP standing-grant payloads, subscription-loss markers, and atomic local-to-server replacement.

test/Capacitor.App.Tests.Unit/PermissionServiceTests.cs

SettingsViewModelTests.csTest notification settings bindings and failures +81/-2

Test notification settings bindings and failures

• Verifies unavailable-service defaults, rapid switch persistence, live updates despite write failures, error clearing, and subscription disposal.

test/Capacitor.App.Tests.Unit/SettingsViewModelTests.cs

SettingsWindowSmokeTests.csSmoke-test notification settings interactions +140/-0

Smoke-test notification settings interactions

• Exercises tab styling, fixed-size layout, disabled controls, pointer clicks, dragging, keyboard focus and toggling, persistence, and window reopening.

test/Capacitor.App.Tests.Unit/SettingsWindowSmokeTests.cs

PermissionWireContractsTests.csTest permission capability wire compatibility +16/-0

Test permission capability wire compatibility

• Verifies capability flags serialize in snake case, round-trip correctly, and remain unknown for older payloads.

test/Capacitor.Cli.Core.Tests.Unit/LocalIpc/PermissionWireContractsTests.cs

AcpPermissionSurfaceTests.csTest ACP grant-scope capability mapping +76/-0

Test ACP grant-scope capability mapping

• Covers one-time, standing, custom, rejected, ambiguous, correlated, and vendor-overridden ACP option sets.

test/Capacitor.Cli.Daemon.Tests.Unit/Services/AcpPermissionSurfaceTests.cs

LocalPermissionBridgeInteractiveTests.csTest hook permission capabilities end to end +18/-0

Test hook permission capabilities end to end

• Verifies Claude and Codex bridge requests advertise the grant scopes their response protocols support.

test/Capacitor.Cli.Daemon.Tests.Unit/Services/LocalPermissionBridgeInteractiveTests.cs

Documentation (4) +114 / -0
README.mdDocument background notification behavior +2/-0

Document background notification behavior

• Explains notification categories, supported actions, idle limitations, macOS authorization, and the requirement that the app remain running.

README.md

CHANGES.mdRecord notification lifecycle decisions +17/-0

Record notification lifecycle decisions

• Documents app-lifetime ownership, request validation and handover behavior, idle transition rules, preference storage, and macOS delivery constraints.

docs/CHANGES.md

2026-09-19-desktop-notifications.mdAdd the completed implementation and verification plan +76/-0

Add the completed implementation and verification plan

• Captures architecture, constraints, review risks, completed tasks, test results, publish checks, native smoke coverage, and resolved review findings.

docs/superpowers/plans/2026-09-19-desktop-notifications.md

2026-09-19-desktop-notifications.mdSpecify actionable desktop notifications +19/-0

Specify actionable desktop notifications

• Defines background-only delivery, permission capabilities, deduplication, reconnect handling, idle semantics, settings, platform behavior, and verification expectations.

docs/superpowers/specs/2026-09-19-desktop-notifications.md

Other (5) +154 / -2
Directory.Packages.propsPin the Avalonia notifications package +1/-0

Pin the Avalonia notifications package

• Adds the centrally managed Avalonia.Labs.Notifications 12.0.2 package version.

Directory.Packages.props

Capacitor.App.csprojReference Avalonia Labs notifications +1/-0

Reference Avalonia Labs notifications

• Adds the notification package used by optional non-macOS native backends.

src/Capacitor.App/Capacitor.App.csproj

Program.csConfigure native notifications during Avalonia startup +3/-2

Configure native notifications during Avalonia startup

• Wraps the application builder with guarded notification platform registration before startup.

src/Capacitor.App/Program.cs

MacNotificationBlock.csSafely bridge managed callbacks to Objective-C blocks +90/-0

Safely bridge managed callbacks to Objective-C blocks

• Implements Apple Block ABI ownership for authorization and completion callbacks, preserving managed state across native copies and containing callback exceptions.

src/Capacitor.App/Services/Notifications/MacNotificationBlock.cs

MacNotificationInterop.csAdd UserNotifications Objective-C interop primitives +59/-0

Add UserNotifications Objective-C interop primitives

• Provides framework loading, Objective-C messaging, runtime class registration, native string and array conversion, and autorelease-pool helpers.

src/Capacitor.App/Services/Notifications/MacNotificationInterop.cs

@qodo-code-review

qodo-code-review Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Unique labels grow native categories ✓ Resolved 🐞 Bug ➹ Performance
Description
Category permanently retains every action-set category in _categories, whose key includes
caller-derived action labels. ACP standing-scope labels can vary between requests, so a long-running
app accumulates native objects and repeatedly registers an ever-growing category set until shutdown.
Code

src/Capacitor.App/Services/Notifications/MacOsDesktopNotificationSink.cs[R104-107]

+        Send(category, Selector("retain"));
+        _categories.Add(key, category);
+        var set = Send(GetClass("NSSet"), Selector("setWithArray:"), Array(_categories.Values));
+        SendVoid(_center, Selector("setNotificationCategories:"), set);
Relevance

●● Moderate

No close category-growth precedent found; dynamic native retention is plausible but performance
impact remains context-dependent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The category identity incorporates every action label, the resulting native category is retained and
added to _categories, and entries are only released during sink disposal. The coordinator uses the
ACP request's supplied standing-scope label as an action label, so labels are not limited to a fixed
application-defined set.

src/Capacitor.App/Services/Notifications/MacOsDesktopNotificationSink.cs[96-108]
src/Capacitor.App/Services/Notifications/MacOsDesktopNotificationSink.cs[138-147]
src/Capacitor.App/Services/Notifications/DesktopNotificationCoordinator.cs[180-190]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The macOS notification sink retains every category generated from action identifiers and labels for the process lifetime. Dynamic ACP action labels can therefore make the native category collection and each subsequent registration grow without bound.

## Fix Focus Areas
- src/Capacitor.App/Services/Notifications/MacOsDesktopNotificationSink.cs[16-21]
- src/Capacitor.App/Services/Notifications/MacOsDesktopNotificationSink.cs[47-54]
- src/Capacitor.App/Services/Notifications/MacOsDesktopNotificationSink.cs[96-108]
- src/Capacitor.App/Services/Notifications/MacOsDesktopNotificationSink.cs[126-129]

## Recommended Fix
Track category references from active pending notifications and release/remove a category when its final notification closes, then re-register the remaining category set. Alternatively, use a bounded set of stable categories while preserving the displayed action behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

2. Local permission replies can race 📘 Rule violation ⌂ Architecture
Description
DesktopNotificationCoordinator.ActivateAsync invokes IPermissionService.PickOptionAsync or
ResolveAsync directly instead of submitting the operation to the app-wide mutation lane. When a
notification answers a local request, PermissionService reaches
LocalControlOps.ResolvePermissionAsync and performs daemon IPC outside the ordering shared by
other desktop mutations.
Code

src/Capacitor.App/Services/Notifications/DesktopNotificationCoordinator.cs[R218-220]

+            var outcome = request.Options is not null
+                ? await _permissions.PickOptionAsync(request, OptionFor(request, action)!, _lifetime.Token)
+                : await _permissions.ResolveAsync(request, action switch {
Relevance

● Weak

Recent PR #770 rejected routing permission mutations through DaemonMutationLane, matching this
serialization concern.

PR-#770

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Compliance rule 2738493 requires every desktop-to-daemon mutation to use the single mutation lane.
The changed coordinator directly calls the permission service, while the lane is documented as the
app-lifetime singleton for daemon mutations and the local permission implementation sends its own
IPC request through LocalControlOps.

Rule 2738493: Route all desktop daemon mutations through the single DaemonMutationLane abstraction
src/Capacitor.App/Services/Notifications/DesktopNotificationCoordinator.cs[218-222]
src/Capacitor.App/Services/Mutation/DaemonMutationLane.cs[12-14]
src/Capacitor.App/Services/PermissionService.cs[77-88]
src/Capacitor.App/Services/PermissionService.cs[138-148]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Desktop notification actions resolve local daemon permission requests directly through `IPermissionService`, bypassing the single app-lifetime daemon mutation lane.

## Fix Focus Areas
- src/Capacitor.App/Services/Notifications/DesktopNotificationCoordinator.cs[218-222]
- src/Capacitor.App/App.axaml.cs[692-706]

## Recommended Fix
Extend the shared `DaemonMutationLane` so local permission resolution can be admitted to its existing ordered queue, then inject that lane-backed operation into `DesktopNotificationCoordinator`. Route both option selection and ordinary permission resolution through it without creating a separate queue or lock.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 64 rules
✅ Cross-repo context — repo relationships
  Explored: repo: kurrent-io/kcap-server (sha: 73b69303)
Review mode: 🧠 Deep: This is a broad, logic-dense cross-platform feature spanning notification orchestration, native macOS interop, background lifecycle, IPC/permission flows, settings, and many independent code paths where redundant review could catch subtle defects.

Grey Divider

Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review history

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/Capacitor.App/Services/Notifications/MacOsDesktopNotificationSink.cs Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae2df69e42

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (pending is null) return null;
var options = request.Options ?? [];
return pending with {
SupportsAllowOnce = PickAllow(options, preferAlways: false) is not null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Withhold Allow for ambiguous local ACP options

When a local ACP request supplies two allow_once options, PickAllow returns the first and this advertises SupportsAllowOnce = true; the notification then exposes a generic Allow button, and MapSettlement silently selects that same first option even though the user never chose between them. The ACP parser permits repeated kinds, and AcpInteractionBridge.TrySelectLeastPrivilegeAllow explicitly treats multiple once-scoped options as ambiguous, so this capability should be false unless exactly one addressable once option exists.

Useful? React with 👍 / 👎.

Comment on lines +193 to +196
static string? OptionFor(PendingPermissionRequest request, string action) {
var kind = action switch { "allow" => "allow_once", "always" => "allow_always", "decline" => "reject_once", _ => null };
if (action == "always" && request.Options?.Count(o => o.Kind == kind) != 1) return null;
return kind is null ? null : request.Options?.FirstOrDefault(o => o.Kind == kind)?.OptionId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require an unambiguous server ACP option

For a server ACP request with multiple allow_once or reject_once options, this picks the first option and presents a generic Allow or Decline action, silently making a choice the user was never shown. Duplicate or blank option IDs are also not rejected here, even though the protocol deserializer permits them and the daemon's AcpInteractionBridge treats such choices as non-addressable; only expose a notification action when its kind resolves to exactly one uniquely addressable option, otherwise direct the user to the app.

Useful? React with 👍 / 👎.

.OrderBy(r => r.Origin).FirstOrDefault();
}

bool HasPending(AgentRow row) => _pending.Any(p => RowFor(p)?.Key == row.Key);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Count stale pending requests before idle alerts

During a hub reconnect, RowFor returns null for every server-lane request unless allowStale is set, so HasPending can report false even when the retained permission notification still maps to this local row on the app's server. If that row transitions from working to awaiting input while _remoteStale is true, the coordinator consequently emits an additional misleading idle notification beside the existing permission alert; pending suppression should use the same stale-tolerant mapping used to retain actionable notices.

Useful? React with 👍 / 👎.

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.

Add push notifications to desktop app

1 participant