# How FluidVoice Manages Per-App Configuration and Different Prompt Sets

> FluidVoice intelligently manages per-app configuration and prompt sets using a hierarchical system. Discover how it customizes Dictate and Edit modes for each application efficiently.

- Repository: [ALTIC/FluidVoice](https://github.com/altic-dev/FluidVoice)
- Tags: internals
- Published: 2026-07-03

---

**FluidVoice uses a hierarchical resolution system where `SettingsStore.promptResolution(for:appBundleID:)` checks for app-specific bindings first, then falls back to global defaults, supporting distinct prompt sets for Dictate and Edit modes across different applications.**

FluidVoice is an open-source AI dictation tool for macOS that enables context-aware transcription through customizable prompt configurations. The repository implements a sophisticated **per-app configuration** system that allows users to assign distinct prompts to individual applications while maintaining global defaults for seamless fallback.

## Understanding the Configuration Architecture

### The SettingsStore Model

At the core of FluidVoice's configuration system lies the `SettingsStore` class, defined in `Sources/Fluid/UI/AISettingsView+AIConfiguration.swift`. This model maintains the relationship between applications and their assigned prompts through two key properties: `appPromptBindings` and `selectedPromptID(for:)`.

The `appPromptBindings` collection stores tuples of `(appBundleID, promptID?)`, where a `nil` value indicates the app should use the global default. The `selectedPromptID(for:)` method retrieves the globally chosen prompt for a given transcription mode.

### PromptMode Enumeration

FluidVoice distinguishes between two transcription contexts using the `SettingsStore.PromptMode` enum:

- `.dictate` — For standard dictation sessions
- `.edit` — For editing existing text

This enumeration allows the system to maintain separate global defaults for each mode while supporting per-app overrides for both contexts.

## How Prompt Resolution Works

The resolution logic centers on the `promptResolution(for:appBundleID:)` method in `SettingsStore`. This method implements a fallback hierarchy:

1. Check if an app-specific binding exists for the provided bundle ID
2. If found, return the bound `promptID`
3. If no binding exists (or if `promptID` is `nil`), return the global `selectedPromptID(for:)` for the current mode

```swift
// Example from DictationE2ETests.swift (line 438)
let mailResolution = settings.promptResolution(for: .dictate, appBundleID: "com.apple.mail")

```

This ensures every transcription context always has a valid prompt, whether customized or default.

## Runtime Flow and UI Integration

The live prompt resolution occurs in [`Sources/Fluid/Views/NotchContentViews.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Views/NotchContentViews.swift). When a user initiates dictation, the view computes the `promptResolutionBundleID` property to determine the current application's bundle identifier.

```swift
// NotchContentViews.swift (line 483, excerpt)
private var promptResolutionBundleID: String? { /* … */ }

// Resolution call
let activePrompt = settings.promptResolution(
    for: self.activePromptMode ?? .dictate,
    appBundleID: self.promptResolutionBundleID
)

```

The UI overlay (triggered by ⌃⌥P by default) displays the global list of profiles while highlighting any app-specific selection. When users choose a profile, the system updates `appPromptBindings` for the active app via `setAppPromptBinding(bundleID:promptID:)`.

## Configuring App-Specific Prompts

To establish or modify per-app configurations, FluidVoice exposes the `setAppPromptBinding` method. Passing a `nil` promptID removes the app-specific override and reverts to global defaults.

```swift
// Update an app‑specific binding from the UI
settings.setAppPromptBinding(
    bundleID: currentAppBundleID,
    promptID: selectedProfile.id   // nil → use global default
)

```

This design allows seamless switching between global and per-app prompt sets without requiring complex configuration workflows.

## Summary

- **Hierarchical resolution**: `SettingsStore.promptResolution(for:appBundleID:)` prioritizes app-specific bindings over global defaults
- **Dual context support**: Separate prompt sets for `.dictate` and `.edit` modes via `PromptMode` enum
- **Dynamic UI integration**: [`NotchContentViews.swift`](https://github.com/altic-dev/FluidVoice/blob/main/NotchContentViews.swift) handles live resolution using `promptResolutionBundleID`
- **Flexible binding management**: `appPromptBindings` stores tuples with optional `promptID` values, where `nil` indicates global fallback
- **Key implementation files**: `AISettingsView+AIConfiguration.swift` contains the model logic, while [`NotchContentViews.swift`](https://github.com/altic-dev/FluidVoice/blob/main/NotchContentViews.swift) handles the runtime resolution

## Frequently Asked Questions

### How does FluidVoice determine which prompt to use for a specific application?

FluidVoice determines the active prompt by calling `SettingsStore.promptResolution(for:appBundleID:)` with the current `PromptMode` and the active application's bundle identifier. According to the `altic-dev/FluidVoice` source code, the method first checks `appPromptBindings` for a specific assignment; if none exists or the binding is `nil`, it returns the global prompt selected for that mode via `selectedPromptID(for:)`.

### Can I set different prompts for dictation versus editing in the same app?

Yes. FluidVoice maintains separate global defaults for each mode through `selectedPromptID(for:)`, which accepts either `.dictate` or `.edit`. When setting app-specific bindings via `setAppPromptBinding`, these respect the currently active mode, allowing distinct prompts for dictation and editing contexts within the same application.

### What happens if I haven't configured a per-app prompt for a specific application?

If no app-specific binding exists in `appPromptBindings`, or if the binding contains a `nil` promptID, FluidVoice automatically falls back to the global default prompt for the current mode. This fallback mechanism ensures uninterrupted transcription functionality without requiring manual configuration for every application.

### Where is the per-app configuration data stored in the codebase?

The configuration logic resides primarily in `Sources/Fluid/UI/AISettingsView+AIConfiguration.swift`, which defines the `SettingsStore` class, `PromptMode` enum, and the `promptResolution` method. The runtime application of these settings occurs in [`Sources/Fluid/Views/NotchContentViews.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Sources/Fluid/Views/NotchContentViews.swift), specifically within the `promptResolutionBundleID` computed property and related UI state management. Integration tests demonstrating this behavior are available in [`Tests/FluidDictationIntegrationTests/DictationE2ETests.swift`](https://github.com/altic-dev/FluidVoice/blob/main/Tests/FluidDictationIntegrationTests/DictationE2ETests.swift).