--- name: overlay-ui description: Notch overlay UI patterns — NSPanel setup, SwiftUI overlay views, screen-sharing hiding, and display adaptation --- # Notch Overlay UI Patterns ## NSPanel Configuration 1. **Use `NSPanel` with `.nonactivatingPanel` style.** This prevents the overlay from stealing focus from Zoom/Meet. The user can keep typing in their meeting app while the overlay is visible. 2. **Critical window properties:** ```swift panel.level = .screenSaver // Above everything, including fullscreen panel.sharingType = .none // INVISIBLE to screen sharing panel.collectionBehavior = [ .canJoinAllSpaces, // Visible on all desktops .fullScreenAuxiliary, // Visible alongside fullscreen apps .stationary // Doesn't move with Space switching ] panel.isOpaque = false panel.backgroundColor = .clear panel.hasShadow = false ``` 3. **`sharingType = .none` is the key feature.** This makes the window completely invisible to ScreenCaptureKit-based screen sharing (Zoom, Google Meet, Teams). The interviewer cannot see the overlay. ## Positioning 4. **Detect notch vs non-notch displays:** ```swift let menuBarHeight = screenFrame.maxY - visibleFrame.maxY let hasNotch = menuBarHeight > 24 // Notch displays have taller menu bars ``` 5. **Center horizontally, position below notch/menu bar.** The overlay should sit just below where the notch ends, centered on screen. 6. **Handle display changes.** Observe `NSApplication.didChangeScreenParametersNotification` to reposition when: - External monitor connected/disconnected - Display arrangement changes - Screen resolution changes 7. **Guard against duplicate panels.** Check `panel?.isVisible == true` before creating a new one in `show()`. ## SwiftUI Overlay View 8. **Use `.ultraThinMaterial` with dark color scheme** for the glassmorphism background. This gives a translucent dark overlay that adapts to the desktop wallpaper. 9. **Use `.clipShape(RoundedRectangle(...))` instead of deprecated `.cornerRadius()`.** The `cornerRadius` modifier is deprecated in recent SwiftUI. 10. **Answer text must scroll.** Wrap answer text in `ScrollView(.vertical)` with a max height. Long answers (detailed mode) will overflow the overlay without this. 11. **Animations on state transitions:** - Question appearing: `.move(edge: .top).combined(with: .opacity)` - Answer appearing: `.opacity` - Hover state: subtle opacity change (0.85 → 1.0) - Keep durations short (0.15-0.2s) — this is a utility UI, not a showcase 12. **Use `foregroundStyle` instead of deprecated `foregroundColor`.** Modern SwiftUI preference. 13. **Font hierarchy for readability at small sizes:** - Status bar: 9pt monospaced - Question label: 9pt monospaced bold (yellow) - Question text: 12pt rounded medium - Answer text: 13pt rounded regular - Mode indicators: 8pt monospaced 14. **Audio level indicator:** 5 bars of increasing height, filled green based on the normalized audio level. Provides visual feedback that the system is hearing audio. ## Content Hosting 15. **Set `panel.contentView = hostingView` directly** instead of adding as a subview. This avoids autoresizing mask issues and is the recommended pattern for NSHostingView in NSPanel.