Skip to content

Add Transcribe with Prompt and simplify dictation prompt controls#256

Merged
altic-dev merged 17 commits intomainfrom
B/pr-227-merge-ready
Apr 4, 2026
Merged

Add Transcribe with Prompt and simplify dictation prompt controls#256
altic-dev merged 17 commits intomainfrom
B/pr-227-merge-ready

Conversation

@altic-dev
Copy link
Copy Markdown
Owner

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:

  • resolve merge drift with current main
  • replace dictation AI toggle with Off / Default / custom prompt selection
  • trim ContentView type body to satisfy lint
  • remove the extra notch Edit / Actions controls

Validation run locally:

  • swiftformat --config .swiftformat Sources
  • swiftlint --strict --config .swiftlint.yml Sources/
  • sh build_incremental.sh

Original contributor work remains preserved in commit history from the original branch.

yelloduxx and others added 16 commits March 28, 2026 19:26
## 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>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +1265 to +1267
if self.promptMode.normalized == .dictate {
self.settings.setDictationPromptSelection(.default)
} else {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@altic-dev altic-dev merged commit 320bd66 into main Apr 4, 2026
2 checks passed
@altic-dev
Copy link
Copy Markdown
Owner Author

#182 fix - this PR ships it. Turn off AI transcription and use the newer button to trigger AI enhanced transcription!

image

hope this fits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants