How FluidVoice Handles Overlay Positioning Around the MacBook Notch

FluidVoice uses NotchOverlayManager to detect hardware notch support via NSScreen auxiliary areas, dynamically selecting between expanded, compact, or bottom overlay presentations based on user preferences and screen capabilities.

The open-source macOS application FluidVoice (altic-dev/FluidVoice) positions its recording interface around the MacBook's display notch using a specialized manager that detects hardware capabilities and respects user-defined preferences. Understanding this architecture reveals how modern Swift apps can integrate with Apple's notch hardware while providing fallback options for non-notched displays.

MacBook Notch Detection and Screen Capability

FluidVoice determines whether the current display supports a hardware notch by inspecting system-provided screen properties in Sources/Fluid/Services/NotchOverlayManager.swift. The manager evaluates the NSScreen auxiliary top areas that exist only on notched MacBook displays.

Detecting Hardware Notch Support

The supportsCompactPresentation(on:) method checks for non-nil auxiliaryTopLeftArea and auxiliaryTopRightArea properties. These values are present exclusively on MacBook models with a display notch, allowing the app to distinguish between notched and standard screens at runtime.

private func supportsCompactPresentation(on screen: NSScreen) -> Bool {
    // A screen with a hardware notch reports non-nil auxiliaryTopLeft/Right areas.
    screen.auxiliaryTopLeftArea?.width != nil && screen.auxiliaryTopRightArea?.width != nil
}

NotchOverlayManager Architecture and Positioning

The NotchOverlayManager class serves as the central coordinator for all overlay positioning. When a recording session begins, the show(audioLevelPublisher:mode:) method evaluates the current configuration and delegates to the appropriate presentation path.

Dynamic Presentation Policy Selection

The manager selects between three distinct presentation strategies based on the return value of supportsCompactPresentation(on:) and the user's notchPresentationMode setting. This logic is encapsulated in refreshNotchPresentationPolicy() (lines 55-82 of NotchOverlayManager.swift), which sets usesCompactPresentation to false for standard mode or true for minimal mode when notch support is detected.

// Simplified flow from lines 60-70 and 200-207
func showInternal() {
    if SettingsStore.shared.overlayPosition == .bottom {
        showBottomOverlay()
        return
    }
    
    let screen = preferredPresentationScreen()
    if supportsCompactPresentation(on: screen) {
        refreshNotchPresentationPolicy()
        // Policy determines expanded vs. compact view hierarchy
    }
}

Creating the Dynamic Notch

When presenting in the notch area, the manager initializes a DynamicNotch from the third-party DynamicNotchKit library. It injects the appropriate SwiftUI view hierarchy based on the selected policy:

let newNotch = DynamicNotch(
    hoverBehavior: [], style: .auto
) {
    NotchExpandedView(audioPublisher: audioLevelPublisher)   // full UI
} compactLeading: {
    NotchCompactLeadingView()                               // small icon
} compactTrailing: {
    NotchCompactTrailingView(audioPublisher: audioLevelPublisher)
} compactBottom: {
    NotchCompactBottomView()
}

Standard vs. Compact Notch Presentation Modes

FluidVoice offers two distinct visual presentations for the notch area, controlled via SettingsStore.shared.notchPresentationMode.

Standard Expanded Presentation

When notchPresentationMode equals .standard and the screen supports a notch, the app displays the full NotchExpandedView defined in Sources/Fluid/Views/NotchContentViews.swift. This view includes the waveform visualization, prompt selector, and streaming preview, filling the width of the notch area.

Compact Minimal Presentation

Setting notchPresentationMode to .minimal triggers a compact policy. In this mode, the manager renders only NotchCompactLeadingView (status icon) and NotchCompactTrailingView (mini waveform), minimizing menu bar obstruction while maintaining quick access to recording controls.

Bottom Overlay Positioning Fallback

For users who prefer the interface away from the menu bar or on non-notched displays, FluidVoice provides a bottom positioning option.

Routing to Bottom Overlay

When SettingsStore.shared.overlayPosition is set to .bottom, the showInternal() method routes directly to showBottomOverlay() (lines 200-207). This bypasses notch detection and instead utilizes BottomOverlayView.swift to render a floating bar at the bottom of the screen.

// From SettingsStore or user toggle
SettingsStore.shared.overlayPosition = .bottom

// Subsequent show() calls route here:
private func showBottomOverlay() {
    // Initializes BottomOverlayWindowController with BottomOverlayView
}

Configuring Notch Settings via SettingsStore

User preferences persist through SettingsStore.swift in Sources/Fluid/Persistence/SettingsStore.swift. The notchPresentationMode property bridges the Settings UI and the overlay manager.

var notchPresentationMode: NotchPresentationMode {
    get {
        guard let raw = defaults.string(forKey: Keys.notchPresentationMode),
              let mode = NotchPresentationMode(rawValue: raw) else { return .standard }
        return mode
    }
    set {
        defaults.set(newValue.rawValue, forKey: Keys.notchPresentationMode)
    }
}

The SettingsView.swift file provides the Picker interface (lines 1367-1374) allowing users to toggle between "Regular notch" (.standard) and "Compact notch" (.minimal), writing the selection to UserDefaults via the SettingsStore keys.

Summary

  • NotchOverlayManager detects hardware notch support by checking NSScreen.auxiliaryTopLeftArea and auxiliaryTopRightArea in Sources/Fluid/Services/NotchOverlayManager.swift.
  • Presentation policies include .standard (expanded UI with full controls), .minimal (compact icon and waveform), and bottom overlay positioning.
  • Dynamic selection occurs at runtime based on the screen containing the mouse pointer, determined by preferredPresentationScreen(), and current SettingsStore values.
  • Bottom fallback activates when overlayPosition is set to .bottom, using BottomOverlayView.swift instead of the notch overlay.
  • View hierarchy is managed through NotchContentViews.swift for notch content and DynamicNotchKit for the system-level notch window integration.

Frequently Asked Questions

How does FluidVoice detect if my MacBook has a display notch?

FluidVoice checks NSScreen.auxiliaryTopLeftArea?.width and auxiliaryTopRightArea?.width in the supportsCompactPresentation(on:) method within NotchOverlayManager.swift. These properties return non-nil values only on MacBook models with a hardware notch, allowing the app to adapt its UI dynamically based on actual display capabilities.

What is the difference between standard and compact notch presentation?

Standard mode displays the full NotchExpandedView with the waveform, prompt selector, and streaming preview filling the notch width. Compact mode (.minimal) renders only NotchCompactLeadingView and NotchCompactTrailingView, showing a small status icon and mini waveform that minimize obstruction of the menu bar while keeping recording controls accessible.

Can I move the overlay away from the notch to the bottom of the screen?

Yes. Setting SettingsStore.shared.overlayPosition = .bottom causes NotchOverlayManager to route calls to showBottomOverlay() instead of the notch overlay. This utilizes BottomOverlayView.swift to display the interface as a floating bar at the bottom of the screen, useful for non-notched displays or user preference.

Which file controls the overlay positioning logic?

The primary logic resides in Sources/Fluid/Services/NotchOverlayManager.swift, which coordinates between SettingsStore.swift (user preferences), the DynamicNotchKit library (notch window rendering), and the view files in Sources/Fluid/Views/ including NotchContentViews.swift and BottomOverlayView.swift.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →