Skip to content

Notifications

The notification system provides a way to display transient messages to users. Notifications appear as floating cards in the corner of the screen and can include action buttons, timeouts, and a slide-out drawer for reviewing all notifications.

Overview

The notification system consists of:

ComponentPurpose
NotificationPanelContainer that hosts content and displays notifications
NotificationIconBell icon (🔔) that shows count and toggles the drawer
NotificationThe notification data with title, body, actions, and lifecycle
NotificationStackManages the collection of active notifications

Basic Setup

To enable notifications in your app, wrap your content in a NotificationPanel inside a ZStack:

csharp

Layout Pattern

The ZStackVStackNotificationPanel pattern ensures notifications float above your content. Place the NotificationIcon in your header/menu bar for easy access.

Posting Notifications

Post notifications from any event handler using e.Context.Notifications.Post():

csharp
ctx.Button("Save").OnClick(e => {
    SaveFile();
    e.Context.Notifications.Post(
        new Notification("Saved!", "File saved successfully"));
})

Notification Properties

Create notifications with a title and optional body:

csharp
// Title only
new Notification("Operation complete")

// Title and body
new Notification("Download Complete", "file.zip (2.4 MB) downloaded successfully")

Timeouts

By default, notifications stay visible until dismissed. Add a timeout to auto-hide them:

csharp
new Notification("Auto-save", "Document saved")
    .Timeout(TimeSpan.FromSeconds(5))

When a notification times out:

  • It disappears from the floating view
  • It remains in the notification drawer
  • The OnTimeout handler is called (if set)

Permanent Notifications

Omit .Timeout() for notifications that require user attention:

csharp
// This stays visible until the user dismisses it
new Notification("Action Required", "Please review the pending changes")
    .PrimaryAction("Review", async ctx => OpenReview())

Action Buttons

Notifications can have a primary action and multiple secondary actions displayed in a split button:

csharp

Primary Action

The main action button, prominently displayed:

csharp
new Notification("New Message", "You have a new message from Alice")
    .PrimaryAction("Read", async ctx => {
        await OpenMessageAsync();
        ctx.Dismiss(); // Optionally dismiss after action
    })

Secondary Actions

Additional actions appear in a dropdown menu:

csharp
new Notification("File Updated", "config.json was modified")
    .PrimaryAction("View Diff", async ctx => ViewDiff())
    .SecondaryAction("Revert", async ctx => RevertChanges())
    .SecondaryAction("Ignore", async ctx => ctx.Dismiss())

Action Context

Action handlers receive a NotificationActionContext with:

PropertyDescription
NotificationThe notification this action belongs to
CancellationTokenToken for cancelling async operations
InputTriggerAccess to app services (focus, popups, etc.)
Dismiss()Remove this notification from the stack
csharp
.PrimaryAction("Open", async ctx => {
    await OpenFileAsync(ctx.CancellationToken);
    ctx.Dismiss(); // Remove notification after action
})

Lifecycle Events

Handle notification lifecycle events with OnTimeout and OnDismiss:

csharp

OnTimeout

Called when the notification auto-hides after its timeout:

csharp
new Notification("Reminder", "Meeting in 5 minutes")
    .Timeout(TimeSpan.FromMinutes(5))
    .OnTimeout(async ctx => {
        // Log that the user saw the reminder
        Analytics.Track("reminder_shown");
    })

OnDismiss

Called when the notification is dismissed (by user or programmatically):

csharp
new Notification("Undo Available", "Item deleted")
    .Timeout(TimeSpan.FromSeconds(10))
    .OnDismiss(async ctx => {
        // Cleanup - the undo window has closed
        await PermanentlyDeleteAsync();
    })

The Notification Drawer

The drawer shows all notifications (both active and timed-out). Access it by:

  • Clicking the notification icon (🔔)
  • Pressing Alt+N from anywhere in the app

When the drawer opens:

  • Floating notifications move into the drawer
  • All notifications are visible in a scrollable list
  • Click outside or press Escape to close

Drawer Behavior

SettingMethodDescription
Float over content.WithDrawerFloats(true)Drawer overlays content (default)
Push content aside.WithDrawerFloats(false)Content resizes when drawer opens
csharp
v.NotificationPanel(content)
    .WithDrawerFloats(false) // Drawer docks instead of floating

Notification Icon

The NotificationIcon displays the bell and notification count:

csharp
bar.NotificationIcon()

Customization

csharp
// Custom bell character (for terminals without emoji support)
bar.NotificationIcon("*")

// Hide the count badge
bar.NotificationIcon().WithCount(false)

Panel Configuration

Configure the notification panel behavior:

csharp
v.NotificationPanel(content)
    .WithMaxFloating(5)        // Show up to 5 floating notifications (default: 3)
    .WithOffset(4, 2)          // Offset from corner (x=4, y=2)
    .WithAnimation(false)      // Disable progress bar animation
    .WithDrawerFloats(true)    // Drawer floats over content

Configuration Options

MethodDefaultDescription
WithMaxFloating(n)3Maximum floating notifications visible
WithOffset(x, y)(2, 1)Offset from top-right corner
WithAnimation(bool)trueAnimate timeout progress bars
WithDrawerFloats(bool)trueDrawer floats vs pushes content

Keyboard Shortcuts

KeyAction
Alt+NToggle notification drawer
EscapeClose drawer (when open)
TabNavigate between notification buttons
EnterActivate focused button

Notification Card Layout

Each notification card displays:

┌────────────────────────────────────┐
│ Title                        [ × ] │  ← Dismiss button
│ Body text (if present)             │
│ [ Primary ▼ ]                      │  ← Action button (if present)
│ ▓▓▓▓▓▓▓▓▓░░░░░                    │  ← Timeout progress (floating only)
└────────────────────────────────────┘
  • Title: Always displayed prominently
  • Body: Optional additional text
  • Dismiss (×): Removes the notification entirely
  • Action button: Split button for primary + secondary actions
  • Progress bar: Shows time remaining (floating cards only)

Complete Example

Here's a complete example showing all notification features:

csharp
using Hex1b;

await using var terminal = Hex1bTerminal.CreateBuilder()
    .WithHex1bApp((app, options) => ctx => ctx.ZStack(z => [
        z.VStack(v => [
            // Header with notification icon
            v.HStack(bar => [
                bar.Button("File"),
                bar.Button("Edit"),
                bar.Text("").FillWidth(),
                bar.NotificationIcon()
            ]),
            
            // Main content wrapped in notification panel
            v.NotificationPanel(
                v.VStack(content => [
                    content.Button("Save").OnClick(e => {
                        e.Context.Notifications.Post(
                            new Notification("Saved", "Document saved")
                                .Timeout(TimeSpan.FromSeconds(3)));
                    }),
                    
                    content.Button("Delete").OnClick(e => {
                        e.Context.Notifications.Post(
                            new Notification("Deleted", "Item moved to trash")
                                .Timeout(TimeSpan.FromSeconds(10))
                                .PrimaryAction("Undo", async ctx => {
                                    // Restore the item
                                    ctx.Dismiss();
                                })
                                .OnDismiss(async ctx => {
                                    // Permanently delete after dismiss
                                }));
                    }),
                    
                    content.Button("Error").OnClick(e => {
                        // No timeout - requires user action
                        e.Context.Notifications.Post(
                            new Notification("Error", "Operation failed")
                                .PrimaryAction("Retry", async ctx => {
                                    // Retry the operation
                                    ctx.Dismiss();
                                })
                                .SecondaryAction("View Details", async ctx => {
                                    // Show error details
                                }));
                    })
                ])
            ).Fill()
        ])
    ]))
    .Build();

await terminal.RunAsync();
  • Button - Trigger notifications from button clicks
  • SplitButton - Used for notification action buttons
  • InfoBar - Persistent status information

Released under the MIT License.