How FluidVoice Displays Its Status in the Menu Bar

FluidVoice uses a native NSStatusItem managed by the MenuBarManager service to display real-time dictation status, updating a dedicated statusMenuItem title and optional button icons whenever the speech pipeline changes state.

FluidVoice is an open-source macOS dictation application that provides immediate visual feedback in the system menu bar. The app implements a custom MenuBarManager class that bridges the automatic speech recognition (ASR) pipeline with AppKit's NSStatusBar API, ensuring users always know whether the app is idle, listening, or processing audio.

The MenuBarManager Service

Creating the NSStatusItem

In Sources/Fluid/Services/MenuBarManager.swift, the manager instantiates a system status item during initialization. The implementation uses NSStatusItem.squareLength to allocate space for the icon in the menu bar.

statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
statusItem?.menu = self.menu

The statusMenuItem Reference

The manager maintains a dedicated NSMenuItem instance called statusMenuItem (referenced around line 494) that serves as the primary text display for current state information. This item is inserted at the top of the dropdown menu and updated dynamically as the dictation pipeline changes state.

if let statusMenuItem = statusMenuItem {
    menu.addItem(statusMenuItem)
}

Status Update Flow

Initialization in fluidApp.swift

The application entry point in Sources/Fluid/fluidApp.swift creates the manager as a state object and injects it into the environment, making it accessible throughout the view hierarchy.

@StateObject private var menuBarManager = MenuBarManager()

var body: some Scene {
    WindowGroup {
        ContentView()
            .environmentObject(self.menuBarManager)
    }
}

Observing ASR State Changes

Sources/Fluid/ContentView.swift observes the ASR service and propagates status changes to the menu bar manager. When the speech recognizer transitions between authorized, listening, processing, and idle states, the view calls menuBarManager.setStatus(_:) to refresh the display.

// Propagating state change to menu bar
menuBarManager.setStatus(currentStatusString)

Mapping Status Strings in NotchContentViews.swift

Human-readable status labels like "Listening", "Processing…", and "Idle" are defined in Sources/Fluid/Views/NotchContentViews.swift. These strings are reused by MenuBarManager to maintain consistency between the notch UI and the menu bar display.

Implementation Details

When the dictation pipeline transitions between states, MenuBarManager.setStatus(_:) updates both the tooltip and the visible menu item text.

func setStatus(_ status: String) {
    statusMenuItem?.title = status
    statusItem?.button?.toolTip = status
    
    // Optional visual feedback via icon
    if status == "Listening" {
        statusItem?.button?.image = NSImage(systemSymbolName: "mic.fill", accessibilityDescription: nil)
    } else {
        statusItem?.button?.image = NSImage(systemSymbolName: "mic", accessibilityDescription: nil)
    }
}

The status item button may also display dynamic images to provide additional visual cues—for example, showing a filled microphone icon when actively listening—by modifying statusItem.button?.image alongside the text updates.

Summary

  • MenuBarManager.swift creates and owns the NSStatusItem, configuring it with NSStatusItem.squareLength and attaching a dropdown menu.
  • statusMenuItem is a dedicated NSMenuItem that displays the current dictation state as text at the top of the menu.
  • fluidApp.swift initializes the manager as a @StateObject and injects it into the SwiftUI environment for global access.
  • ContentView.swift bridges the ASR service to the menu bar by calling setStatus(_:) whenever the speech pipeline state changes.
  • NotchContentViews.swift provides the canonical status strings used for both the notch overlay and menu bar display, ensuring UI consistency.

Frequently Asked Questions

What NSStatusItem length does FluidVoice use?

FluidVoice initializes the status item using NSStatusItem.squareLength in MenuBarManager.swift, allocating sufficient space for the icon while maintaining a compact footprint in the macOS menu bar.

How does the status text update when dictation starts?

When the ASR service detects a state change, ContentView.swift calls menuBarManager.setStatus(_:) with the new state string. This method immediately updates statusMenuItem.title and the button's toolTip property to reflect the current activity.

Where are the status strings defined?

The human-readable labels for states like "Listening" and "Processing…" are defined in Sources/Fluid/Views/NotchContentViews.swift and reused by MenuBarManager to ensure the menu bar text matches the transient overlay shown in the notch area.

Can the menu bar icon change based on status?

Yes. While the primary status display uses statusMenuItem.title, the implementation can also modify statusItem.button?.image to show different system symbols—such as switching between mic and mic.fill—based on whether the app is actively recording or idle.

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 →