Skip to content

ADHD-friendly, impossible-to-miss full-screen alert system for macOS. Extracted from Chime app.

License

Notifications You must be signed in to change notification settings

uxderrick/ChimeAlert

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ChimeAlert

A full-screen alert system for macOS with multi-monitor support, keyboard shortcuts, and extensive customization options.

Swift 5.9+ Platform License

Features

  • Full-screen alerts with animated pulsating border
  • Multi-monitor support (all monitors, primary only, external only, or mouse location)
  • Sleep/wake recovery handling to prevent UI freezing
  • Keyboard shortcuts (⌘S snooze, ⌘↩ action, Esc dismiss)
  • Sound playback with 5 bundled sounds and rotation support
  • Highly customizable (colors, animations, timings, sounds)
  • Snooze management with configurable limits
  • Delegate pattern for analytics and custom behavior
  • Multiple alert types (meetings, reminders, tasks, custom)

Installation

Swift Package Manager

Add ChimeAlert to your Package.swift dependencies:

dependencies: [
    .package(url: "https://github.com/uxderrick/ChimeAlert.git", from: "1.0.0")
]

Or in Xcode:

  1. File → Add Package Dependencies
  2. Enter repository URL: https://github.com/uxderrick/ChimeAlert.git
  3. Select version and add to target

Quick Start

1. Conform Your Model to AlertItem

import ChimeAlert
import Foundation

struct MyMeeting: AlertItem {
    var id: String
    var title: String
    var startTime: Date
    var endTime: Date
    var notes: String?
    var actionURL: URL?  // Optional action URL (e.g., Zoom link)
    var actionButtonTitle: String? { actionURL != nil ? "Join" : nil }
    var attendees: [AlertAttendee]?
    var isRecurring: Bool
    var recurrenceDescription: String?
    var priority: AlertPriority { .high }
    var type: AlertType { .meeting }
}

2. Configure AlertManager

import ChimeAlert

// Configure global settings
AlertManager.shared.configuration.soundVolume = 0.9
AlertManager.shared.configuration.maxSnoozeAttempts = 5
AlertManager.shared.monitorPreference = .allMonitors

3. Show an Alert

let meeting = MyMeeting(
    id: "meeting-123",
    title: "Q4 Planning Meeting",
    startTime: Date().addingTimeInterval(300), // 5 minutes from now
    endTime: Date().addingTimeInterval(3900),  // 1 hour later
    notes: "Discuss roadmap and budget",
    actionURL: URL(string: "https://zoom.us/j/123456789"),
    attendees: [
        AlertAttendee(name: "Alice Chen", isOrganizer: true),
        AlertAttendee(name: "Bob Smith")
    ],
    isRecurring: false,
    recurrenceDescription: nil
)

AlertManager.shared.showAlert(for: meeting)

Configuration

Visual Customization

// Pulsating border
AlertManager.shared.configuration.borderColors = [.red, .orange]
AlertManager.shared.configuration.pulseDuration = 1.5

// Type-specific gradients
AlertManager.shared.configuration.meetingGradient = [
    Color(red: 0.2, green: 0.4, blue: 0.8),
    Color(red: 0.1, green: 0.2, blue: 0.6)
]

// Animation speeds
AlertManager.shared.configuration.glowDuration = 3.0
AlertManager.shared.configuration.entranceAnimationDuration = 0.5

Sound Configuration

// Enable/disable sound
AlertManager.shared.configuration.soundEnabled = false

// Volume (0.0 to 1.0)
AlertManager.shared.configuration.soundVolume = 0.5

// Sound rotation
AlertManager.shared.configuration.rotateSounds = false

Snooze Behavior

// Default snooze duration (seconds)
AlertManager.shared.configuration.defaultSnoozeInterval = 300 // 5 minutes

// Available snooze options
AlertManager.shared.configuration.snoozeOptions = [60, 300, 600, 1800]

// Maximum snooze attempts before disabling snooze button
AlertManager.shared.configuration.maxSnoozeAttempts = 5

Multi-Monitor Configuration

// Show on all monitors
AlertManager.shared.monitorPreference = .allMonitors

// Show on primary monitor only
AlertManager.shared.monitorPreference = .primaryOnly

// Show on external monitors only
AlertManager.shared.monitorPreference = .externalOnly

// Show on monitor where mouse cursor is located
AlertManager.shared.monitorPreference = .mouseLocation

Delegate Integration

Implement AlertDelegate to handle lifecycle events, track analytics, and customize behavior:

@MainActor
class MyAlertDelegate: AlertDelegate {
    func alertDidShow(_ item: AlertItem) {
        // Track alert display
        Analytics.track("alert_shown", properties: ["title": item.title])
    }

    func alertDidTapAction(_ item: AlertItem) {
        // Handle action button tap (Join/Complete/Open)
        if let url = item.actionURL {
            NSWorkspace.shared.open(url)
        }
    }

    func alertDidSnooze(_ item: AlertItem, duration: TimeInterval) {
        // Track snooze action
        Analytics.track("alert_snoozed", properties: ["duration": duration])
    }

    func alertShouldTrackStats() -> Bool {
        return true // Enable internal stat tracking
    }

    func alertDidTrackStat(event: String, properties: [String: Any]) {
        // Forward to your analytics system
        Analytics.track(event, properties: properties)
    }

    func alertShouldShow(_ item: AlertItem) async -> Bool {
        // Optional: Validate if alert should still be shown
        // Useful for checking external state (e.g., task deleted)
        return true
    }
}

// Set the delegate
AlertManager.shared.delegate = MyAlertDelegate()

Alert Types

ChimeAlert supports multiple alert types with different visual styling:

Meeting (Blue Gradient)

struct Meeting: AlertItem {
    var type: AlertType { .meeting }
    // Blue/green gradient, "Join" button, shows attendees
}

Reminder (Purple Gradient)

struct Reminder: AlertItem {
    var type: AlertType { .reminder }
    // Purple gradient, "Complete" button
}

Task (Red Gradient)

struct Task: AlertItem {
    var type: AlertType { .task }
    // Red gradient, "Complete" button
}

Custom

struct CustomAlert: AlertItem {
    var type: AlertType {
        .custom(
            gradient: [
                ColorComponents(red: 1.0, green: 0.5, blue: 0.0),
                ColorComponents(red: 0.8, green: 0.3, blue: 0.0)
            ],
            iconName: "star.fill"
        )
    }
}

Advanced Features

Trial Badge (Optional)

Display a countdown badge for trial periods:

AlertManager.shared.configuration.trialBadge = TrialBadgeInfo(
    text: "7d trial left",
    color: Color.orange
)

// Remove badge
AlertManager.shared.configuration.trialBadge = nil

Sleep/Wake Recovery

ChimeAlert automatically handles system sleep/wake transitions. To integrate with your app's sleep monitoring:

func setupSleepWakeNotifications() {
    NSWorkspace.shared.notificationCenter.addObserver(
        forName: NSWorkspace.willSleepNotification,
        object: nil,
        queue: .main
    ) { _ in
        AlertManager.isSystemRecovering = true
    }

    NSWorkspace.shared.notificationCenter.addObserver(
        forName: NSWorkspace.didWakeNotification,
        object: nil,
        queue: .main
    ) { _ in
        DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
            AlertManager.isSystemRecovering = false
        }
    }
}

Attribution

This library displays a "Powered by Chime" badge in the bottom-right corner of all alerts. The badge links to usechime.app when clicked.

Badge removal is only available with a commercial license. See Commercial Licensing for details.

Requirements

  • macOS 13.0 (Ventura) or later
  • Swift 5.9 or later
  • Xcode 15.0 or later (for development)

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure swift test passes
  5. Submit a pull request

Commercial Licensing

The open-source version of ChimeAlert requires the "Powered by Chime" attribution badge to remain visible in all deployments. If you need to use ChimeAlert in a commercial application, you must purchase a commercial license.

Why Purchase a Commercial License?

  • Remove the attribution badge - Display your own branding without "Powered by Chime"
  • Professional appearance - No third-party branding in your commercial products
  • Perpetual license - One-time payment, use forever (no recurring fees)
  • Unlimited applications - Use in as many commercial products as you want
  • Support development - Help maintain and improve ChimeAlert

Pricing

Tier Price Best For
Indie License $99 USD Solo developers, indie projects, and businesses with <$100K annual revenue
Enterprise License $499 USD Companies with ≥$100K annual revenue, agencies, and teams

Both tiers include:

  • Badge removal for all your commercial applications
  • Perpetual license (use forever)
  • All future updates
  • Email support

How to Purchase

Contact us at [email protected] with:

  1. Your tier - Indie or Enterprise
  2. Company/individual name - For the license certificate
  3. Use case (optional) - What you're building with ChimeAlert

We'll send you an invoice and license certificate within 1 business day.

License

ChimeAlert is released under the MIT License.

This means:

  • Free for everyone - personal and commercial use allowed
  • Modify and distribute - create derivative works freely
  • No restrictions - use in any project without limitations
  • ⚠️ Attribution badge required - the "Powered by Chime" badge must remain visible (removal only available with commercial license - see Commercial Licensing)

Credits

Extracted from Chime - a meeting reminder app for macOS.


Questions or feedback? Open an issue on GitHub.

About

ADHD-friendly, impossible-to-miss full-screen alert system for macOS. Extracted from Chime app.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  

Languages