| name | island-best-practice |
| description | Conventions, architecture, and design patterns for the boring.notch macOS Dynamic Island app. Use this skill whenever working on any feature, bugfix, or refactor in boring.notch โ including adding new views, managers, settings, animations, or modifying the notch layout. Also consult this when the user asks about how the project works, how to add a new module, or when you need to understand the codebase structure before making changes.
|
boring.notch Best Practices
boring.notch is a macOS app that replaces the MacBook's notch with a dynamic, interactive widget.
It displays music playback, notifications, system HUDs, calendar/weather, a file shelf, and more.
Repository technical architecture (Chinese, contributor-oriented): root ARCHITECTURE.md โ state layers, windowing, sizing, Liquid Glass summary, gestures, shortcuts/speech, and pointers into this skillโs references/. Build, signing, notarization, CI, and Island vs. boringNotch naming: BUILD.md.
References
Detailed module-specific docs live in references/. Consult them when working in the
relevant area:
| Reference | When to read |
|---|
references/design-conventions.md | Colors, typography, spacing, liquid glass mode, glass text modifiers |
references/animation-patterns.md | Spring values, matchedGeometryEffect IDs, transitions, gestures |
references/music-module.md | Waveform coloring rules, canonical animation params, sneak peek |
references/widget-system.md | Home layout, closed notch widgets, forbidden zone, todo/inspiration widgets |
references/window-and-input.md | NSPanel config, hover-out, canBecomeKey, keyboard shortcuts |
references/xcode-integration.md | Adding files to project.pbxproj, group IDs, dependencies |
Project Structure
boringNotch/
โโโ boringNotchApp.swift # App entry, window creation, lifecycle
โโโ ContentView.swift # Root view โ notch shape, background, gestures, state routing
โโโ components/
โ โโโ Notch/ # Core notch UI (BoringHeader, NotchHomeView, NotchSettingsView, etc.)
โ โโโ Calendar/ # Calendar + weather widgets
โ โโโ Shelf/ # Drag & drop file shelf
โ โโโ Settings/ # External settings window
โ โโโ Live activities/ # Download progress, HUD indicators
โ โโโ Music/ # Lyrics, visualizer, slider
โ โโโ Tabs/ # Tab bar (home/shelf/widgets)
โ โโโ Webcam/ # Camera preview
โ โโโ Onboarding/ # First-launch flow
โ โโโ Tips/ # TipKit tips (e.g. TipStore.swift)
โโโ managers/ # Singleton ObservableObject managers
โโโ models/ # BoringViewModel, Constants, data models
โโโ extensions/ # SwiftUI View extensions, helpers
โโโ helpers/ # Utility classes (AppleScript, AppIcons, etc.)
โโโ observers/ # System event observers (media keys, fullscreen, drag)
โโโ sizing/ # Notch dimensions and corner radii
โโโ enums/ # App-wide enums
โโโ animations/ # Animation definitions
โโโ private/ # CGSSpace (auto-synced in Xcode)
โโโ metal/ # Metal shaders (audio visualizer)
โโโ menu/ # Status bar menu
โโโ Shortcuts/ # KeyboardShortcuts definitions (e.g. ShortcutConstants.swift)
โโโ utils/ # Logging
Architecture
State Management โ Three Layers
-
BoringViewModel โ Per-screen notch state. Owns notchState (.open/.closed),
notchSize, and transient UI state (hover, drop targeting, camera). Passed via
@EnvironmentObject to all views.
-
BoringViewCoordinator โ Global singleton (BoringViewCoordinator.shared). Controls
which view is displayed (currentView: NotchViews), sneak peek / expanding view state,
first-launch flow, and screen selection. Accessed via @ObservedObject in views.
-
Defaults (sindresorhus/Defaults) โ Persisted user preferences. All keys live in
Constants.swift under extension Defaults.Keys. Use @Default(.keyName) for reactive
bindings in views, Defaults[.keyName] for read-only access.
Adding a New Setting
- Add the key to
Constants.swift:
static let myFeature = Key<Bool>("myFeature", default: false)
- Use
@Default(.myFeature) var myFeature in views that react to changes.
- Add a toggle in
NotchSettingsView (in-notch) and/or SettingsView (external window).
Manager / Singleton Pattern
Every system-level service follows this pattern:
@MainActor
class FooManager: NSObject, ObservableObject {
static let shared = FooManager()
@Published var someState: Type = defaultValue
private override init() {
super.init()
}
func startMonitoring() { ... }
func stopMonitoring() { ... }
}
Key rules:
- Always
@MainActor if the manager drives UI via @Published.
- Use
static let shared โ never create multiple instances.
- Access in views with
@ObservedObject var foo = FooManager.shared.
- Prefer
NSObject base class when interfacing with system APIs (CoreAudio, CoreLocation, etc.).
View Composition
ContentView is the root. It builds the notch layout in layers:
ContentView (body)
โโโ ZStack โ VStack
โโโ NotchLayout() # Content inside the notch shape
โ โโโ [closed] state-specific views (music live activity, battery, HUD, notification, face)
โ โโโ [open] BoringHeader # Top bar with tabs, notch cutout, action buttons
โ โโโ [closed] ClosedNotchWidgetBar # Configurable widget indicators (market, pomodoro)
โ โโโ [open] switch currentView:
โ โโโ .home โ NotchHomeView # Music player + calendar/weather + pomodoro
โ โโโ .shelf โ ShelfView # File shelf
โ โโโ .clip โ DynaClipView # Mini file browser (pinned folders; DynaClipManager)
โ โโโ .settings โ NotchSettingsView
โ โโโ .widgets โ WidgetHubView # Widget management
โ โโโ .market โ MarketTickerView # Crypto/stock/gold prices
โ โโโ .translation โ TranslationView
โ โโโ .todoList โ TodoListView # Quick todo list (fn+T)
โ โโโ .inspiration โ InspirationView # Inspiration recorder (fn+I)
โโโ Chin rectangle (click target below notch)
When adding a new top-level view to the notch:
- Add a case to
NotchViews enum in enums/generic.swift.
- Add the
case to the switch coordinator.currentView in ContentView.NotchLayout().
- If the view needs a different notch size, update
vm.notchSize when switching to it.
- If it needs keyboard input, see
references/window-and-input.md for canBecomeKey setup.
Enum Conventions
- Simple state enums: bare cases (
NotchState, NotchViews, Style)
- User-facing enums with persistence:
String raw values + Defaults.Serializable
- Add
CaseIterable, Identifiable when used in pickers
- Associated values for complex state:
CalendarSelectionState, EventType
Common Patterns
Conditional Modifiers
.conditionalModifier(someCondition) { view in
view.someModifier()
}
Defined in ConditionalModifier.swift. Use instead of ternary-in-modifier for complex logic.
Sneak Peek / Expanding View
Transient HUDs (volume, brightness, notifications) use coordinator.toggleSneakPeek().
This shows a brief overlay in the closed notch, then auto-dismisses after a timeout.
Checklist for New Features
- Create the manager (if needed) in
managers/ following the singleton pattern.
- Add settings keys to
Constants.swift.
- Create views in the appropriate
components/ subdirectory.
- Wire into
ContentView โ either in NotchLayout() for closed-state displays, or in
the switch coordinator.currentView for open-state views.
- Add
project.pbxproj entries for all new files (see references/xcode-integration.md).
- Add toggles to
NotchSettingsView and/or SettingsView.
- Use existing animation values (see
references/animation-patterns.md) โ don't invent new springs.
- Test both open and closed notch states, and with liquid glass on/off.
- If the new view is full-height (like settings), add it to
scrollLocked set in
handleUpGesture and to the needsTall check in onChange(of: currentView).
- For widgets: add to
WidgetHubView with enable toggle. Add a HomeWidget case
for home view placement. Add to homeWidgets default order in HomeWidget.defaultOrder.