Skip to content

Add Prompt Mode: condense dictation into a tight prompt (closes #196)#268

Open
swimmer-303 wants to merge 1 commit into
zachlatta:mainfrom
swimmer-303:split/prompt-mode
Open

Add Prompt Mode: condense dictation into a tight prompt (closes #196)#268
swimmer-303 wants to merge 1 commit into
zachlatta:mainfrom
swimmer-303:split/prompt-mode

Conversation

@swimmer-303

@swimmer-303 swimmer-303 commented Jul 11, 2026

Copy link
Copy Markdown

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

    • Added Prompt Mode to condense rambling dictation into concise prompts while preserving the full intent.
    • Choose whether Prompt Mode is off, always enabled, or automatically enabled when dictating into AI tools.
    • Processing occurs in the same cleanup pass without adding latency.
    • Added Prompt Mode controls and guidance to General settings.
  • Documentation

    • Updated the README and changelog with Prompt Mode details.

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)
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Prompt 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.

Changes

Prompt Mode

Layer / File(s) Summary
Prompt mode state and target detection
Sources/AppState.swift
Adds the Prompt Mode setting, persistence, initialization, AI-target detection, and conditional forwarding of the condensation flag.
Prompt condensation processing
Sources/PostProcessingService.swift
Threads the condensation option through primary and fallback processing and appends prompt-condensation instructions to the system prompt.
Settings and feature documentation
Sources/SettingsView.swift, README.md, CHANGELOG.md
Adds the Prompt Mode settings picker and documents its available behavior and AI-app detection.
Estimated code review effort: 3 (Moderate) ~20 minutes

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
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding Prompt Mode to condense dictation into a prompt.
Linked Issues check ✅ Passed The PR implements Prompt Mode with off/always/auto behavior, AI-app detection, and intent-preserving condensation before paste.
Out of Scope Changes check ✅ Passed The changes stay focused on Prompt Mode in docs, settings, state, and post-processing; no clear unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch split/prompt-mode

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 win

Auto Prompt Mode never condenses on retry.

restoredContext hardcodes bundleIdentifier: nil, windowTitle: nil, but shouldCondenseForPrompt/isAITargetContext (Lines 2477-2508) rely on exactly those fields to detect an AI target in .auto mode. Since item.contextBundleIdentifier and item.contextWindowTitle are already persisted (used later when rebuilding updatedItem), 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7427ca9 and ff1a529.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • README.md
  • Sources/AppState.swift
  • Sources/PostProcessingService.swift
  • Sources/SettingsView.swift

Comment thread Sources/AppState.swift
Comment on lines +2477 to +2508
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) }
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 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:


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.

Comment on lines +1310 to +1322
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)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 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.

Suggested change
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().

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: "Prompt Mode" — summarize/condense dictated text before pasting

1 participant