feat: add setting to keep transcript in clipboard after pasting#107
feat: add setting to keep transcript in clipboard after pasting#107HNKNTA wants to merge 4 commits intoamicalhq:mainfrom
Conversation
Add a "Keep transcript in clipboard" toggle to Advanced Settings that skips restoring the original clipboard contents after dictation paste, leaving the transcribed text available for subsequent manual pastes. Addresses issue amicalhq#100.
📝 WalkthroughWalkthroughAdds a new preference keepTranscriptInClipboard across schema, defaults, migrations, UI, settings service, recording/paste logic, types, and native paste implementations (macOS/Windows) so pasteText can optionally preserve clipboard contents. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant SettingsUI as Settings UI
participant SettingsService as Settings Service
participant RecordingMgr as Recording Manager
participant NativeBridge as Native Bridge (macOS/Windows)
participant Clipboard as System Clipboard
User->>SettingsUI: Toggle "Keep Transcript in Clipboard"
SettingsUI->>SettingsService: updatePreferences(keepTranscriptInClipboard)
SettingsService-->>SettingsUI: ack
Note over RecordingMgr,SettingsService: Later: user requests paste
RecordingMgr->>SettingsService: getPreferences()
SettingsService-->>RecordingMgr: preferences (keepTranscriptInClipboard)
RecordingMgr->>NativeBridge: pasteText(transcript, keepInClipboard)
rect rgba(100, 150, 255, 0.5)
Note over NativeBridge,Clipboard: keepInClipboard = true
NativeBridge->>Clipboard: Paste text (do not restore original)
end
rect rgba(100, 200, 150, 0.5)
Note over NativeBridge,Clipboard: keepInClipboard = false
NativeBridge->>Clipboard: Paste text
NativeBridge->>NativeBridge: Schedule restore original clipboard content
end
Clipboard-->>User: Clipboard updated
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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 Tip You can disable poems in the walkthrough.Disable the |
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)
apps/desktop/src/main/managers/recording-manager.ts (1)
773-804:⚠️ Potential issue | 🟠 MajorDo not block paste when preference lookup fails.
A failure reading preferences currently aborts paste. This creates a functional regression under transient settings/DB errors. Fallback to
falseand continue pasting.💡 Proposed fix
try { const nativeBridge = this.serviceManager.getService("nativeBridge"); - const settingsService = - this.serviceManager.getService("settingsService"); - const preferences = await settingsService.getPreferences(); - const keepInClipboard = - preferences?.keepTranscriptInClipboard ?? false; + let keepInClipboard = false; + try { + const settingsService = + this.serviceManager.getService("settingsService"); + const preferences = await settingsService.getPreferences(); + keepInClipboard = preferences?.keepTranscriptInClipboard ?? false; + } catch (error) { + logger.main.warn( + "Failed to load keepTranscriptInClipboard, using default=false", + { error: error instanceof Error ? error.message : String(error) }, + ); + } logger.main.info("Pasting transcription to active application", { textLength: transcription.length, keepInClipboard, }); if (nativeBridge) { void nativeBridge .call("pasteText", { transcript: transcription, keepInClipboard, })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/managers/recording-manager.ts` around lines 773 - 804, The current preference lookup via settingsService.getPreferences() can throw and abort the paste; change the flow in the paste routine in recording-manager (around serviceManager.getService("settingsService") and getPreferences()) to catch errors from getPreferences() separately, log a warning, and fall back to keepInClipboard = false so the paste still proceeds; ensure you still call nativeBridge.call("pasteText", { transcript: transcription, keepInClipboard }) and preserve the existing error handling for nativeBridge.call failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/renderer/main/pages/settings/advanced/index.tsx`:
- Around line 63-73: The mutation handler updatePreferencesMutation currently
logs and shows a toast on error but doesn't revert optimistic UI changes; update
the onError callback in updatePreferencesMutation (and the other mutation at
lines ~149-154) to either refetch the preference cache via
utils.settings.getPreferences.invalidate() or explicitly roll back the local
toggle state used by the settings page component (the state/handler that flips
the switch) so the switch reflects the server value after a failed update;
ensure the rollback runs before showing the error toast to keep the UI
consistent.
In `@packages/native-helpers/windows-helper/src/RpcHandler.cs`:
- Around line 309-310: The current RPC handler calls
accessibilityService.PasteText(parameters.Transcript, keepInClipboard, out var
errorMessage) and always returns the fixed string "Pasted successfully", which
discards any non-fatal warning in errorMessage; update the response creation in
the method that handles the PasteText RPC (RpcHandler.cs) to surface
errorMessage when it is non-empty even if success==true—e.g., append or include
the warning text in the success message or add an explicit warning field in the
response object—so when parameters.KeepInClipboard is false and restore fails
the client sees the warning instead of losing the errorMessage.
---
Outside diff comments:
In `@apps/desktop/src/main/managers/recording-manager.ts`:
- Around line 773-804: The current preference lookup via
settingsService.getPreferences() can throw and abort the paste; change the flow
in the paste routine in recording-manager (around
serviceManager.getService("settingsService") and getPreferences()) to catch
errors from getPreferences() separately, log a warning, and fall back to
keepInClipboard = false so the paste still proceeds; ensure you still call
nativeBridge.call("pasteText", { transcript: transcription, keepInClipboard })
and preserve the existing error handling for nativeBridge.call failures.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
packages/native-helpers/windows-helper/src/Models/Generated/Models.csis excluded by!**/generated/**
📒 Files selected for processing (15)
apps/desktop/src/db/app-settings.tsapps/desktop/src/db/schema.tsapps/desktop/src/db/settings-migrations/index.tsapps/desktop/src/i18n/locales/en.jsonapps/desktop/src/i18n/locales/es.jsonapps/desktop/src/i18n/locales/ja.jsonapps/desktop/src/main/managers/recording-manager.tsapps/desktop/src/renderer/main/pages/settings/advanced/index.tsxapps/desktop/src/services/settings-service.tsapps/desktop/src/trpc/routers/settings.tspackages/native-helpers/swift-helper/Sources/SwiftHelper/AccessibilityService.swiftpackages/native-helpers/swift-helper/Sources/SwiftHelper/RpcHandler.swiftpackages/native-helpers/windows-helper/src/RpcHandler.cspackages/native-helpers/windows-helper/src/Services/AccessibilityService.cspackages/types/src/schemas/methods/paste-text.ts
… paste warnings Invalidate preferences query cache on updatePreferencesMutation error so the toggle resyncs to the server value. Also fix Windows RPC handler to surface clipboard restore warnings instead of discarding them when paste succeeds with a non-fatal error.
Main branch already used v8 for dictation language defaults, so rename our keepTranscriptInClipboard migration to v9 and bump settings version. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/db/settings-migrations/v9.ts`:
- Around line 5-13: The migration currently treats input as an object and always
overwrites keepTranscriptInClipboard; make it defensive by verifying data is a
non-null object and that preferences is an object before mutating it, defaulting
keepTranscriptInClipboard only when it is missing (not when it exists).
Concretely, in v9.ts check that oldData is an object and set preferences =
typeof oldData.preferences === 'object' && oldData.preferences !== null ?
oldData.preferences : {}; then only add keepTranscriptInClipboard: false if
!('keepTranscriptInClipboard' in preferences); finally return the merged object
(preserving any existing value) so non-object inputs are untouched and existing
flags are not overwritten.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8469c36b-4ee6-494f-a647-729b2f565d18
⛔ Files ignored due to path filters (1)
packages/native-helpers/windows-helper/src/Models/Generated/Models.csis excluded by!**/generated/**
📒 Files selected for processing (12)
apps/desktop/src/db/app-settings.tsapps/desktop/src/db/schema.tsapps/desktop/src/db/settings-migrations/index.tsapps/desktop/src/db/settings-migrations/v9.tsapps/desktop/src/i18n/locales/en.jsonapps/desktop/src/i18n/locales/es.jsonapps/desktop/src/i18n/locales/ja.jsonapps/desktop/src/main/managers/recording-manager.tsapps/desktop/src/services/settings-service.tsapps/desktop/src/trpc/routers/settings.tspackages/native-helpers/swift-helper/Sources/SwiftHelper/RpcHandler.swiftpackages/native-helpers/windows-helper/src/RpcHandler.cs
🚧 Files skipped from review as they are similar to previous changes (6)
- apps/desktop/src/db/schema.ts
- apps/desktop/src/main/managers/recording-manager.ts
- apps/desktop/src/i18n/locales/ja.json
- apps/desktop/src/trpc/routers/settings.ts
- apps/desktop/src/i18n/locales/es.json
- apps/desktop/src/db/settings-migrations/index.ts
| const oldData = data as AppSettingsData; | ||
| const preferences = oldData.preferences ?? {}; | ||
|
|
||
| return { | ||
| ...oldData, | ||
| preferences: { | ||
| ...preferences, | ||
| keepTranscriptInClipboard: false, | ||
| }, |
There was a problem hiding this comment.
Make this migration defensive and non-destructive.
This currently assumes object-shaped input and force-overwrites keepTranscriptInClipboard to false. Prefer defaulting only when missing and guarding non-object input.
Suggested patch
export function migrateToV9(data: unknown): AppSettingsData {
- const oldData = data as AppSettingsData;
- const preferences = oldData.preferences ?? {};
+ const oldData =
+ data && typeof data === "object"
+ ? (data as Partial<AppSettingsData>)
+ : {};
+ const preferences = oldData.preferences ?? {};
return {
...oldData,
preferences: {
...preferences,
- keepTranscriptInClipboard: false,
+ keepTranscriptInClipboard:
+ preferences.keepTranscriptInClipboard ?? false,
},
- };
+ } as AppSettingsData;
}📝 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.
| const oldData = data as AppSettingsData; | |
| const preferences = oldData.preferences ?? {}; | |
| return { | |
| ...oldData, | |
| preferences: { | |
| ...preferences, | |
| keepTranscriptInClipboard: false, | |
| }, | |
| const oldData = | |
| data && typeof data === "object" | |
| ? (data as Partial<AppSettingsData>) | |
| : {}; | |
| const preferences = oldData.preferences ?? {}; | |
| return { | |
| ...oldData, | |
| preferences: { | |
| ...preferences, | |
| keepTranscriptInClipboard: | |
| preferences.keepTranscriptInClipboard ?? false, | |
| }, | |
| } as AppSettingsData; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/db/settings-migrations/v9.ts` around lines 5 - 13, The
migration currently treats input as an object and always overwrites
keepTranscriptInClipboard; make it defensive by verifying data is a non-null
object and that preferences is an object before mutating it, defaulting
keepTranscriptInClipboard only when it is missing (not when it exists).
Concretely, in v9.ts check that oldData is an object and set preferences =
typeof oldData.preferences === 'object' && oldData.preferences !== null ?
oldData.preferences : {}; then only add keepTranscriptInClipboard: false if
!('keepTranscriptInClipboard' in preferences); finally return the merged object
(preserving any existing value) so non-object inputs are untouched and existing
flags are not overwritten.
|
Any chances to merge it? I had to merge main in the branch to make it actual |
Add a "Keep transcript in clipboard" toggle to Advanced Settings that skips restoring the original clipboard contents after dictation paste, leaving the transcribed text available for subsequent manual pastes.
Addresses issue #100.
Summary by CodeRabbit
New Features
Localization
Chores