Skip to content

feat: Add Notification System with In-App, OS-Level, and Sound Options #134

@olivermontes

Description

@olivermontes

Summary

Implement a comprehensive notification system that allows users to stay informed about Levante's activity, especially for long-running tasks. Users can configure their preferred notification style through Settings.

Problem

When users leave Levante running in the background (e.g., waiting for AI to complete a complex task), they have no way to know when:

  • A chat response has completed
  • An MCP tool has finished executing
  • An error has occurred
  • A background process needs attention

Solution

Multi-layered notification system with granular user control.

Notification Levels

Level Description Visual
None No notifications -
In-App Only Bell icon with badge counter 🔔 (3)
OS Notification System notification center Native toast
OS + Sound System notification with audio Native toast + sound

Requirements

1. Notification Types & Events

type NotificationEvent = 
  | 'chat_complete'           // AI finished responding
  | 'chat_error'              // AI response failed
  | 'mcp_tool_complete'       // MCP tool finished
  | 'mcp_tool_error'          // MCP tool failed
  | 'mcp_server_disconnected' // MCP server lost connection
  | 'export_complete'         // File export finished
  | 'update_available'        // New app version available
  | 'announcement';           // New announcement (see #133)

interface NotificationPayload {
  id: string;
  event: NotificationEvent;
  title: string;
  body: string;
  icon?: string;
  timestamp: Date;
  read: boolean;
  action?: {
    type: 'navigate' | 'open_url' | 'dismiss';
    target?: string;
  };
}

2. Settings Configuration

interface NotificationPreferences {
  enabled: boolean;                    // Master switch
  
  // Per-event configuration
  events: {
    [key in NotificationEvent]: {
      level: 'none' | 'in_app' | 'os' | 'os_sound';
      sound?: string;                  // Custom sound file (optional)
    };
  };
  
  // Global options
  showBadge: boolean;                  // App icon badge (dock/taskbar)
  badgeCount: 'all' | 'unread';        // What to count
  playSound: boolean;                  // Global sound toggle
  soundVolume: number;                 // 0-100
  
  // Do Not Disturb
  quietHours: {
    enabled: boolean;
    start: string;                     // "22:00"
    end: string;                       // "08:00"
  };
}

3. In-App Notification Center (Bell Icon)

┌─────────────────────────────────────┐
│  🔔 Notifications           Clear All │
├─────────────────────────────────────┤
│  ● Chat completed                    │
│    "Análisis de código finalizado"  │
│    hace 2 minutos                    │
├─────────────────────────────────────┤
│  ● MCP Tool finished                 │
│    filesystem: read_file completed   │
│    hace 5 minutos                    │
├─────────────────────────────────────┤
│  ○ Export complete                   │
│    chat_export.json saved            │
│    hace 1 hora                       │
└─────────────────────────────────────┘
    ● = unread   ○ = read

Bell Icon States:

  • 🔔 Normal (no unread)
  • 🔔 (3) Badge with count
  • 🔔 Animated shake on new notification

4. OS-Level Notifications

macOS:

import { Notification } from 'electron';

new Notification({
  title: 'Levante - Chat Complete',
  body: 'Your AI response is ready',
  icon: path.join(__dirname, 'icon.png'),
  silent: \!preferences.playSound
}).show();

Windows: Native Windows notifications via Electron

Linux: libnotify integration

5. App Icon Badge

macOS Dock:

app.dock.setBadge('3');        // Show count
app.dock.setBadge('');         // Clear
app.dock.bounce('informational'); // Bounce once

Windows Taskbar:

mainWindow.setOverlayIcon(
  nativeImage.createFromPath('badge.png'),
  'New notifications'
);
mainWindow.flashFrame(true);   // Flash taskbar

Linux: Unity/GNOME badge support where available

6. Sound System

Built-in Sounds:

  • notification.wav - Default notification
  • success.wav - Task completed
  • error.wav - Error occurred
  • subtle.wav - Low priority

Custom Sounds:

  • Allow users to select custom audio files
  • Validate file format (wav, mp3, ogg)
  • Preview sound in settings

Settings UI

┌─────────────────────────────────────────────────┐
│  ⚙️ Notification Settings                        │
├─────────────────────────────────────────────────┤
│                                                 │
│  [✓] Enable notifications                       │
│                                                 │
│  ─── Notification Style ───                     │
│                                                 │
│  Chat Responses:                                │
│  [▼ OS Notification + Sound]                    │
│                                                 │
│  MCP Tool Execution:                            │
│  [▼ In-App Only]                                │
│                                                 │
│  Errors & Warnings:                             │
│  [▼ OS Notification + Sound]                    │
│                                                 │
│  Updates & Announcements:                       │
│  [▼ In-App Only]                                │
│                                                 │
│  ─── Sound ───                                  │
│                                                 │
│  [✓] Play notification sounds                   │
│  Volume: [━━━━━━━●━━━] 70%                      │
│  Sound: [▼ Default] [▶ Preview]                 │
│                                                 │
│  ─── App Badge ───                              │
│                                                 │
│  [✓] Show badge on app icon                     │
│  Badge shows: (•) All  ( ) Unread only          │
│                                                 │
│  ─── Quiet Hours ───                            │
│                                                 │
│  [ ] Enable quiet hours                         │
│  From: [22:00] To: [08:00]                      │
│                                                 │
└─────────────────────────────────────────────────┘

Suggestions

Phase 1 (MVP)

  • NotificationService in main process
  • Basic in-app notification center (bell icon + dropdown)
  • OS notifications via Electron Notification API
  • Simple settings: on/off toggle per category
  • Badge counter on bell icon

Phase 2 (Enhanced)

  • App icon badge (macOS dock, Windows taskbar)
  • Sound notifications with built-in sounds
  • Per-event notification level configuration
  • Notification history (persist last 50)
  • Mark as read / Clear all functionality

Phase 3 (Advanced)

  • Custom sounds support
  • Quiet hours / Do Not Disturb mode
  • Taskbar flash on Windows
  • Dock bounce on macOS
  • Click notification to navigate to relevant content

Phase 4 (Polish)

  • Notification grouping (batch similar notifications)
  • Smart notifications (don't notify if window is focused)
  • Keyboard shortcuts (Ctrl+Shift+N to open notification center)
  • Notification actions (quick reply, retry, etc.)
  • Statistics: notifications per day/week

Technical Considerations

Notification Service Architecture

// Main process
class NotificationService {
  private history: NotificationPayload[] = [];
  private unreadCount = 0;
  
  async notify(event: NotificationEvent, data: Partial<NotificationPayload>) {
    const prefs = await preferencesService.getNotifications();
    
    // Check quiet hours
    if (this.isQuietHours(prefs)) return;
    
    // Get event-specific settings
    const eventConfig = prefs.events[event];
    if (eventConfig.level === 'none') return;
    
    const notification: NotificationPayload = {
      id: generateId(),
      event,
      timestamp: new Date(),
      read: false,
      ...data
    };
    
    // Store in history
    this.history.unshift(notification);
    this.unreadCount++;
    
    // In-app notification (always if enabled)
    if (eventConfig.level \!== 'none') {
      this.sendToRenderer('notification:new', notification);
      this.updateBadge();
    }
    
    // OS notification
    if (eventConfig.level === 'os' || eventConfig.level === 'os_sound') {
      this.showOSNotification(notification, eventConfig.level === 'os_sound');
    }
  }
  
  private updateBadge() {
    // macOS
    if (process.platform === 'darwin') {
      app.dock.setBadge(this.unreadCount > 0 ? String(this.unreadCount) : '');
    }
    // Windows
    if (process.platform === 'win32' && this.mainWindow) {
      // Set overlay icon or flash
    }
  }
}

Focus-Aware Notifications

// Don't show OS notification if app is focused
private shouldShowOSNotification(): boolean {
  const focused = BrowserWindow.getFocusedWindow();
  return \!focused || \!focused.isFocused();
}

Integration Points

Hook notifications into existing services:

Persistence

// Store notification preferences in ui-preferences.json
// Store notification history in memory (clear on app restart)
// Optional: persist history to SQLite for analytics

UX Considerations

Smart Defaults

  • First-time users: In-app notifications only
  • Power users can enable OS notifications
  • Sound off by default (respect user focus)

Notification Fatigue Prevention

  • Group rapid notifications ("5 tools completed")
  • Rate limit: max 1 OS notification per 10 seconds
  • Auto-collapse similar notifications

Accessibility

  • Screen reader support for notifications
  • Visual + audio feedback options
  • High contrast badge for visibility

Related Issues


Labels: enhancement, feature, ux, electron

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestv1.8.0Planned for v1.8.0 release

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions