Haze is a personal macOS calendar app that stays on the desktop as a frosted-glass floating panel. It is not a WidgetKit widget; it is a real SwiftUI/AppKit app, so the desktop panel can handle clicks, popovers, text input, event editing, range events, and a separate main app window.
The project is built for personal/public GitHub distribution, not App Store distribution.
- Desktop-level frosted calendar panel
- Month navigation, Today button, and date popovers
- Add, delete, time, and multi-day range events from the panel
- Main app window for larger calendar management
- Shared SwiftData store between the panel and main app
- Category colors: purple, teal, coral, amber, blue
- Connected range-event bars in the main app and desktop panel
- Menu bar controls for opening the app, showing/hiding the panel, launch at login, and quit
- macOS 14 Sonoma or later
- Xcode 15 or later
- Swift 5.9 or later
Haze needs Accessibility permission so the desktop panel can route clicks correctly while staying behind normal app windows.
Haze is not sandboxed. The desktop panel relies on macOS Accessibility and a global mouse event tap, which are not compatible with a normal App Store-style sandboxed permission model.
Enable it here:
System Settings > Privacy & Security > Accessibility > Haze
Current build note:
- Accessibility: required
- Input Monitoring: not required
If you rebuild, move, rename, or reinstall the app bundle, macOS may treat it as a new app. In that case, remove the old Haze entry from Accessibility and add/enable the new one again.
On first launch, Haze keeps checking Accessibility for a short time after showing the permission prompt. Once the permission is granted, the existing desktop panel should start accepting clicks without requiring a restart.
Clone the repository:
git clone https://github.com/chh41/Haze.git
cd HazeOpen the project in Xcode:
open Haze.xcodeprojThen select the Haze scheme and run.
You can also build from the terminal:
# From the repository root
xcodebuild -scheme Haze -configuration Debug buildAfter a successful build, run the generated Haze.app from Xcode's Products folder or the DerivedData build output.
If you want to keep the app like a normal local app, copy the built Haze.app into /Applications. If you move, rebuild, or replace the app bundle, macOS may ask for Accessibility permission again.
- Haze stores calendar data locally with SwiftData at
~/Library/Application Support/Haze/default.store. The project does not add a separate encryption layer, so use FileVault if the device may contain sensitive schedules. - Older builds may have used
~/Library/Application Support/default.store; Haze migrates that legacy store to the app-specific path on first launch when the new store does not already exist. - If Haze cannot open the persistent store, it does not open the calendar panel for editing. This prevents schedules from appearing to save only in temporary memory.
- Haze does not need network access and does not include API keys or server credentials.
- Debug routing logs are disabled by default. They only compile to the real Haze log if
HAZE_VERBOSE_LOGGINGis explicitly added to Swift active compilation conditions. - Do not remove Gatekeeper quarantine from app bundles you did not build yourself or receive from a trusted source.
Haze includes a menu bar option:
Haze menu bar icon > 로그인 시 자동 실행
This uses macOS SMAppService.mainApp, so the app can register or unregister itself as a login item. You can also verify it in:
System Settings > General > Login Items & Extensions
If panel clicks suddenly stop working after testing screenshots, try:
killall screencaptureuiIf desktop panel clicks stop working after a rebuild:
- Quit Haze.
- Remove the old Haze entry from Accessibility.
- Reopen the newly built app.
- Add/enable Haze in Accessibility again.
If Haze shows a persistent storage warning, quit the app and check that the app can read and write:
~/Library/Application Support/Haze/default.store
If the Dock or Settings icon looks stale, macOS icon cache may be holding the old icon. Quit the app, rebuild, remove the old app bundle if needed, and reopen the new build.
- Main spec:
SPEC.md - Phase 1A widget notes:
docs/phase-1a-widget-summary.md - Phase 1A debugging retrospective:
docs/phase-1a-debugging-retrospective.md - Phase 2 main app notes:
docs/phase-2-main-app-summary.md - Phase 3 distribution/setup notes:
docs/phase-3-distribution-setup.md

