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 withNSStatusItem.squareLengthand attaching a dropdown menu. - statusMenuItem is a dedicated
NSMenuItemthat displays the current dictation state as text at the top of the menu. - fluidApp.swift initializes the manager as a
@StateObjectand 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →