Add Transcribe with Prompt and simplify dictation prompt controls#256
Add Transcribe with Prompt and simplify dictation prompt controls#256
Conversation
## Transcribe with Prompt Adds a new hotkey mode that starts a recording session with a specific AI prompt applied — without changing the user's global prompt selection. Settings → Shortcuts gets a new "Transcribe with Prompt" row with a key binder and a prompt picker (populated from Dictate prompt profiles). The hotkey supports both toggle and hold modes, matching the existing Command Mode and Rewrite Mode behavior. The selected prompt is passed directly to processTextWithAI(overrideSystemPrompt:), so the global selectedDictationPromptID is never mutated — no save/restore hacks. The overlay shows the active prompt name during recording via a new promptModeOverrideProfileName property on NotchContentState that is set on start and cleared on stop. Changed files: - GlobalHotkeyManager: new promptMode case, matching the command/rewrite pattern - ContentView: promptModeCallback, ActiveRecordingMode.promptMode, shortcut bindings - SettingsStore: PromptModeShortcutEnabled, PromptModeHotkeyShortcut, PromptModeSelectedPromptID keys - SettingsView: new shortcut row + prompt picker - NotchContentViews: @published var promptModeOverrideProfileName - BottomOverlayView: selectedPromptLabel checks override before resolving from settings ## AI gate: respect custom prompt selection DictationAIPostProcessingGate.isConfigured() previously returned false when AI Processing was toggled off, even if the user had a custom prompt selected. This caused dictation with a custom prompt to skip AI entirely. Fix: if a custom prompt is selected for the Dictate mode, treat it as an implicit opt-in to AI processing regardless of the toggle state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract inline prompt mode handling from handleKeyEvent into three private methods to satisfy SwiftLint cyclomatic_complexity and function_body_length limits: - handlePromptModeKeyDown(keyCode:modifiers:) -> Bool - handlePromptModeKeyUp(keyCode:) -> Bool - handlePromptModeFlagsChanged(keyCode:isModifierPressed:) -> Bool No behavior change. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ention 1. Build full system prompt for prompt mode override: Use stripBasePrompt + combineBasePrompt to construct the complete system prompt (base instructions + profile body), matching how normal profile selection works. Previously profile.prompt was passed raw, which could drop baseline dictation constraints. 2. Clear prompt override on all cancel/abort paths: Move override cleanup into clearActiveRecordingMode() so any call to stopWithoutTranscription (Escape, cancel, mode-disable) also clears promptModeOverrideText and promptModeOverrideProfileName. Previously only stopAndProcessTranscription cleared the override, leaving a stale value that would leak into the next normal dictation session. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…RecordingMode) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
P1: Guard prompt-mode AI behind provider configuration The previous condition (isConfigured() || promptOverride != nil) would try to run AI even when the provider has no API key, causing an error string to be injected as dictation output. Now uses isProviderConfigured() to check that the provider is actually usable before forcing AI for prompt-mode runs. P2: Clear prompt override on any mode switch away from promptMode Move the override cleanup into setActiveRecordingMode so that switching from promptMode to any other mode (dictate, command, edit) during an active recording also clears promptModeOverrideText and the overlay name. Previously only clearActiveRecordingMode and stopAndProcessTranscription cleared it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ict with Command Mode Prompt mode was defaulting to Right Command (keyCode 54), same as Command Mode. Since prompt mode is checked first in GlobalHotkeyManager, this made Command Mode unreachable with default settings. Changed default to Right Shift (keyCode 60) which is unused by any other mode. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NotchContentState handlers (AI toggle, copy, reprocess, undo, etc.) were nilled out in ContentView.onDisappear, breaking the overlay when the settings window was closed. Since ContentView is a struct (value type), there is no retain cycle risk — the cleanup was unnecessary. Removed the nil assignments so the overlay remains fully functional regardless of settings window state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…w to extension Move completeOnboardingIfPossible, labelFor, checkAccessibilityPermissions, openAccessibilitySettings, restartApp, and startAccessibilityPolling into a ContentView extension to satisfy type_body_length SwiftLint limit after rebasing onto v1.5.10. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The bottom overlay already displayed the selected prompt name during Transcribe with Prompt recording, but the top notch overlay always showed "Default". Apply the same promptModeOverrideProfileName check to NotchExpandedView.selectedPromptLabel. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The top (notch) overlay now has the same interactive controls as the bottom overlay: - Prompt chip: click to open prompt picker; during Transcribe with Prompt mode the selected profile can be changed live (updates the override for the current recording session via a new onPromptModeProfileChangeRequested callback) - Mode chip: click to switch between Dictate and Edit recording modes - AI chip: click to toggle AI processing on/off - Actions chip: click to open a menu with Reprocess Last, Copy Last, and Undo AI actions Menus are rendered as overlays so they never affect the notch's layout dimensions, preventing the edge-artifact flicker seen with ZStack-based inline menus. The notch view has a fixed width (216 pt) to further stabilise the layout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…verlay Replaces the fragile `promptModeOverrideProfileName != nil` check with a dedicated `isPromptModeActive` flag, so the correct callback branch is taken even when the user selects "Default" (which sets the name to nil). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ddb7014ec3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if self.promptMode.normalized == .dictate { | ||
| self.settings.setDictationPromptSelection(.default) | ||
| } else { |
There was a problem hiding this comment.
Update active prompt-mode override from bottom prompt menu
When prompt mode is active, dictation output is driven by promptModeOverrideText in ContentView, but this menu path only mutates persisted dictation settings. Selecting Default here therefore does not update the in-flight prompt-mode override (and can unexpectedly change normal dictation behavior for later sessions), so the current recording may still use the previous prompt. This action should route through the same prompt-mode callback used by the notch prompt menu when isPromptModeActive is true.
Useful? React with 👍 / 👎.
|
#182 fix - this PR ships it. Turn off AI transcription and use the newer button to trigger AI enhanced transcription!
hope this fits. |

Supersedes #227 because the original PR head branch lives on a fork that we cannot push to from the maintainer side, even though local merge/conflict resolution and follow-up fixes are ready.
This branch contains the original PR work plus maintainer follow-ups:
mainOff / Default / customprompt selectionContentViewtype body to satisfy lintEdit/ActionscontrolsValidation run locally:
swiftformat --config .swiftformat Sourcesswiftlint --strict --config .swiftlint.yml Sources/sh build_incremental.shOriginal contributor work remains preserved in commit history from the original branch.