Add Prompt Mode: condense dictation into a tight prompt (closes #196)#268
Add Prompt Mode: condense dictation into a tight prompt (closes #196)#268swimmer-303 wants to merge 1 commit into
Conversation
Off (default) / Always / Only in AI apps. Auto mode detects Claude, ChatGPT, Cursor, and similar tools from the frontmost app's bundle ID and window title. Condensation is a section appended to the cleanup system prompt in the same LLM pass, so it adds no latency, and the instruction-execution guard still applies. Edit Mode is unaffected. Closes zachlatta#196 (cherry picked from commit 6e0bd94)
📝 WalkthroughWalkthroughPrompt Mode adds Off, Always, and AI-app auto-detection settings, persists the selection, conditionally condenses transcripts during post-processing, and documents the feature in the README and changelog. ChangesPrompt Mode
Sequence Diagram(s)sequenceDiagram
participant AppState
participant PostProcessingService
participant LLM
AppState->>AppState: evaluate promptMode and active app context
AppState->>PostProcessingService: postProcess(transcript, condenseToPrompt)
PostProcessingService->>LLM: process transcript with optional condensation instructions
LLM-->>PostProcessingService: return processed result
PostProcessingService-->>AppState: return PostProcessingResult
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Sources/AppState.swift (1)
1164-1183: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAuto Prompt Mode never condenses on retry.
restoredContexthardcodesbundleIdentifier: nil, windowTitle: nil, butshouldCondenseForPrompt/isAITargetContext(Lines 2477-2508) rely on exactly those fields to detect an AI target in.automode. Sinceitem.contextBundleIdentifieranditem.contextWindowTitleare already persisted (used later when rebuildingupdatedItem), retrying a dictation that was originally condensed in Auto mode will silently fall back to plain cleanup.🔧 Proposed fix
let restoredContext = AppContext( appName: nil, - bundleIdentifier: nil, - windowTitle: nil, + bundleIdentifier: item.contextBundleIdentifier, + windowTitle: item.contextWindowTitle, selectedText: nil,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/AppState.swift` around lines 1164 - 1183, Restore the persisted target metadata when constructing restoredContext: replace the nil bundleIdentifier and windowTitle values with item.contextBundleIdentifier and item.contextWindowTitle. Keep the values consistent with the later updatedItem reconstruction so shouldCondenseForPrompt and isAITargetContext can correctly detect AI targets during Auto-mode retries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/AppState.swift`:
- Around line 2477-2508: The window-title marker in isAITargetContext is too
broad because “copilot” can match unrelated windows; replace it with a more
specific Copilot identifier or require stronger contextual matching, while
preserving the existing AI bundle checks and other title markers.
In `@Sources/SettingsView.swift`:
- Around line 1310-1322: Add an accessibility label to the Prompt Mode Picker in
the HStack containing Text("Condense dictation"), matching the existing
overlayDisplaySection pattern by applying .accessibilityLabel("Prompt mode")
after .labelsHidden().
---
Outside diff comments:
In `@Sources/AppState.swift`:
- Around line 1164-1183: Restore the persisted target metadata when constructing
restoredContext: replace the nil bundleIdentifier and windowTitle values with
item.contextBundleIdentifier and item.contextWindowTitle. Keep the values
consistent with the later updatedItem reconstruction so shouldCondenseForPrompt
and isAITargetContext can correctly detect AI targets during Auto-mode retries.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 8578066b-fa49-4975-981b-5d7ee7b5b61b
📒 Files selected for processing (5)
CHANGELOG.mdREADME.mdSources/AppState.swiftSources/PostProcessingService.swiftSources/SettingsView.swift
| private func shouldCondenseForPrompt(context: AppContext) -> Bool { | ||
| switch promptMode { | ||
| case .off: | ||
| return false | ||
| case .always: | ||
| return true | ||
| case .auto: | ||
| return Self.isAITargetContext( | ||
| bundleIdentifier: context.bundleIdentifier, | ||
| windowTitle: context.windowTitle | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| /// Heuristic for "the user is dictating a prompt into an AI tool": | ||
| /// known AI desktop app bundle IDs, or a window title mentioning a | ||
| /// major AI product (covers browser tabs and terminal sessions). | ||
| static func isAITargetContext(bundleIdentifier: String?, windowTitle: String?) -> Bool { | ||
| let aiBundleIdentifiers: Set<String> = [ | ||
| "com.anthropic.claudefordesktop", | ||
| "com.openai.chat", | ||
| "com.todesktop.230313mzl4w4u92", // Cursor | ||
| "com.exafunction.windsurf", | ||
| ] | ||
| if let bundleIdentifier, aiBundleIdentifiers.contains(bundleIdentifier) { | ||
| return true | ||
| } | ||
| let title = (windowTitle ?? "").lowercased() | ||
| let markers = ["claude", "chatgpt", "chat gpt", "gemini", "copilot", "perplexity"] | ||
| return markers.contains { title.contains($0) } | ||
| } | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
What is the current bundle identifier of the ChatGPT desktop app for macOS?
💡 Result:
The current bundle identifier for the official ChatGPT desktop application on macOS is com.openai.chat [1][2][3]. This identifier is consistently used in the application's configuration, package management, and system integration workflows [2][3][4].
Citations:
- 1: https://community.openai.com/t/chat-gpt-desktop-app-for-mac/744613/35
- 2: https://github.com/Homebrew/homebrew-cask/blob/0067a88c0f3d765ec1e8ea70d1968472378eb376/Casks/c/chatgpt.rb
- 3: https://github.com/Homebrew/homebrew-cask/blob/47817d96ba845ccedf309ed3f1e820076cecd0ba/Casks/c/chatgpt.rb
- 4: https://appcatalog.cloud/apps/chatgpt
Tighten the copilot window-title marker.
com.openai.chat is the correct ChatGPT bundle ID, so the bundle list looks fine. The broad copilot title match can still fire on unrelated windows and cause unintended condensation; require a stronger signal or narrow the marker.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Sources/AppState.swift` around lines 2477 - 2508, The window-title marker in
isAITargetContext is too broad because “copilot” can match unrelated windows;
replace it with a more specific Copilot identifier or require stronger
contextual matching, while preserving the existing AI bundle checks and other
title markers.
| HStack { | ||
| Text("Condense dictation") | ||
| .font(.system(size: 13)) | ||
| Spacer() | ||
| Picker("", selection: $appState.promptMode) { | ||
| Text("Off").tag(PromptModeSetting.off) | ||
| Text("Always").tag(PromptModeSetting.always) | ||
| Text("Only in AI apps").tag(PromptModeSetting.auto) | ||
| } | ||
| .labelsHidden() | ||
| .pickerStyle(.menu) | ||
| .frame(maxWidth: 240) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Add an accessibility label to the Prompt Mode picker.
The picker uses .labelsHidden() with an empty string label, so VoiceOver users hear only the selected value without context. The overlayDisplaySection picker (line 1109) addresses this with .accessibilityLabel("Show on"). Apply the same pattern here.
♿ Proposed fix
Picker("", selection: $appState.promptMode) {
Text("Off").tag(PromptModeSetting.off)
Text("Always").tag(PromptModeSetting.always)
Text("Only in AI apps").tag(PromptModeSetting.auto)
}
.labelsHidden()
+ .accessibilityLabel("Condense dictation")
.pickerStyle(.menu)
.frame(maxWidth: 240)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| HStack { | |
| Text("Condense dictation") | |
| .font(.system(size: 13)) | |
| Spacer() | |
| Picker("", selection: $appState.promptMode) { | |
| Text("Off").tag(PromptModeSetting.off) | |
| Text("Always").tag(PromptModeSetting.always) | |
| Text("Only in AI apps").tag(PromptModeSetting.auto) | |
| } | |
| .labelsHidden() | |
| .pickerStyle(.menu) | |
| .frame(maxWidth: 240) | |
| } | |
| HStack { | |
| Text("Condense dictation") | |
| .font(.system(size: 13)) | |
| Spacer() | |
| Picker("", selection: $appState.promptMode) { | |
| Text("Off").tag(PromptModeSetting.off) | |
| Text("Always").tag(PromptModeSetting.always) | |
| Text("Only in AI apps").tag(PromptModeSetting.auto) | |
| } | |
| .labelsHidden() | |
| .accessibilityLabel("Condense dictation") | |
| .pickerStyle(.menu) | |
| .frame(maxWidth: 240) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Sources/SettingsView.swift` around lines 1310 - 1322, Add an accessibility
label to the Prompt Mode Picker in the HStack containing Text("Condense
dictation"), matching the existing overlayDisplaySection pattern by applying
.accessibilityLabel("Prompt mode") after .labelsHidden().
Split out from #254.
Off (default) / Always / Only in AI apps. Condenses rambling dictation into a tight, intent-preserving prompt before pasting. Auto mode detects Claude, ChatGPT, Cursor, and similar tools from the frontmost app's bundle ID and window title. Condensation is a section appended to the cleanup system prompt in the same LLM pass, so it adds no latency; the instruction-execution guard still applies, and Edit Mode is unaffected.
Closes #196
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation