Preserve clipboard contents after paste
- Save clipboard before pasting transcript (text, images, any content) - Restore original clipboard contents after paste completes - User's copied content is no longer lost after dictation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ A simple menu bar app for voice dictation using OpenAI Whisper (local, offline).
|
||||
- 🎤 Global hotkey (⌃⌥D) to start/stop recording
|
||||
- 🔒 Fully offline - uses local Whisper model
|
||||
- ⚡ Automatic paste into any focused app
|
||||
- 📋 Clipboard preservation - your copied content is restored after paste
|
||||
- ⚙️ Settings window with model selection dropdown
|
||||
- 📥 Built-in model downloader with progress indicator
|
||||
- 🚀 Launch at login support
|
||||
|
||||
@@ -825,12 +825,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDownloadDelegate {
|
||||
// MARK: - Paste
|
||||
func pasteText(_ text: String) {
|
||||
let pasteboard = NSPasteboard.general
|
||||
|
||||
// Save current clipboard contents
|
||||
let savedItems = saveClipboard()
|
||||
|
||||
// Set transcript to clipboard
|
||||
pasteboard.clearContents()
|
||||
pasteboard.setString(text, forType: .string)
|
||||
|
||||
NSLog("Transcribed: \(text)")
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
// Simulate Cmd+V
|
||||
let source = CGEventSource(stateID: .hidSystemState)
|
||||
|
||||
let keyDown = CGEvent(keyboardEventSource: source, virtualKey: 0x09, keyDown: true)
|
||||
@@ -841,11 +847,49 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDownloadDelegate {
|
||||
keyUp?.flags = .maskCommand
|
||||
keyUp?.post(tap: .cghidEventTap)
|
||||
|
||||
// Restore original clipboard after a short delay
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||
self.restoreClipboard(savedItems)
|
||||
}
|
||||
|
||||
self.statusItem.button?.title = "🎤"
|
||||
self.updateStatus("Ready")
|
||||
if self.playSounds { NSSound(named: "Glass")?.play() }
|
||||
}
|
||||
}
|
||||
|
||||
func saveClipboard() -> [[NSPasteboard.PasteboardType: Data]] {
|
||||
let pasteboard = NSPasteboard.general
|
||||
var savedItems: [[NSPasteboard.PasteboardType: Data]] = []
|
||||
|
||||
for item in pasteboard.pasteboardItems ?? [] {
|
||||
var itemData: [NSPasteboard.PasteboardType: Data] = [:]
|
||||
for type in item.types {
|
||||
if let data = item.data(forType: type) {
|
||||
itemData[type] = data
|
||||
}
|
||||
}
|
||||
if !itemData.isEmpty {
|
||||
savedItems.append(itemData)
|
||||
}
|
||||
}
|
||||
return savedItems
|
||||
}
|
||||
|
||||
func restoreClipboard(_ savedItems: [[NSPasteboard.PasteboardType: Data]]) {
|
||||
guard !savedItems.isEmpty else { return }
|
||||
|
||||
let pasteboard = NSPasteboard.general
|
||||
pasteboard.clearContents()
|
||||
|
||||
for itemData in savedItems {
|
||||
let item = NSPasteboardItem()
|
||||
for (type, data) in itemData {
|
||||
item.setData(data, forType: type)
|
||||
}
|
||||
pasteboard.writeObjects([item])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Main
|
||||
|
||||
Reference in New Issue
Block a user