@@ -17,6 +17,19 @@ final class TextInjector {
1717 /// Virtual key code for the V key
1818 private let kVK_V : CGKeyCode = 9
1919
20+ // MARK: - Types
21+
22+ /// Deep copy of a single pasteboard item's data across all its types
23+ private struct PasteboardItemSnapshot {
24+ /// Map from pasteboard type to raw data
25+ let dataByType : [ ( NSPasteboard . PasteboardType , Data ) ]
26+ }
27+
28+ /// Deep copy of the entire pasteboard state
29+ private struct PasteboardSnapshot {
30+ let items : [ PasteboardItemSnapshot ]
31+ }
32+
2033 // MARK: - Public API
2134
2235 /// Inject text at the current cursor position in any application
@@ -40,8 +53,10 @@ final class TextInjector {
4053
4154 let pasteboard = NSPasteboard . general
4255
43- // Save current clipboard text
44- let previousText = preserveClipboard ? pasteboard. string ( forType: . string) : nil
56+ // Deep-copy current clipboard state before we overwrite it.
57+ // NSPasteboardItem objects are invalidated when the pasteboard is cleared,
58+ // so we must extract the raw data eagerly.
59+ let snapshot = preserveClipboard ? captureSnapshot ( pasteboard) : nil
4560
4661 // Set transcribed text to clipboard
4762 pasteboard. clearContents ( )
@@ -54,15 +69,65 @@ final class TextInjector {
5469 simulatePaste ( )
5570
5671 // Restore clipboard after paste completes
57- if preserveClipboard, let previous = previousText {
72+ if preserveClipboard {
5873 DispatchQueue . main. asyncAfter ( deadline: . now( ) + clipboardRestoreDelay) {
59- pasteboard. clearContents ( )
60- pasteboard. setString ( previous, forType: . string)
74+ if let snapshot = snapshot {
75+ self . restoreSnapshot ( snapshot, to: pasteboard)
76+ } else {
77+ // Previous clipboard was empty; clear the transcribed text
78+ pasteboard. clearContents ( )
79+ }
80+ NSLog ( " [TextInjector] Clipboard restored " )
6181 }
6282 }
6383 }
6484 }
6585
86+ // MARK: - Clipboard Snapshot Management
87+
88+ /// Deep-copy every item and type from the pasteboard into plain `Data` values.
89+ /// This must be called *before* `clearContents()` because NSPasteboardItem
90+ /// objects are invalidated when the pasteboard changes.
91+ private func captureSnapshot( _ pasteboard: NSPasteboard ) -> PasteboardSnapshot ? {
92+ guard let pasteboardItems = pasteboard. pasteboardItems, !pasteboardItems. isEmpty else {
93+ return nil
94+ }
95+
96+ var itemSnapshots : [ PasteboardItemSnapshot ] = [ ]
97+
98+ for item in pasteboardItems {
99+ var dataByType : [ ( NSPasteboard . PasteboardType , Data ) ] = [ ]
100+ for type in item. types {
101+ if let data = item. data ( forType: type) {
102+ dataByType. append ( ( type, data) )
103+ }
104+ }
105+ if !dataByType. isEmpty {
106+ itemSnapshots. append ( PasteboardItemSnapshot ( dataByType: dataByType) )
107+ }
108+ }
109+
110+ guard !itemSnapshots. isEmpty else { return nil }
111+ return PasteboardSnapshot ( items: itemSnapshots)
112+ }
113+
114+ /// Write a previously captured snapshot back to the pasteboard.
115+ private func restoreSnapshot( _ snapshot: PasteboardSnapshot , to pasteboard: NSPasteboard ) {
116+ pasteboard. clearContents ( )
117+
118+ var newItems : [ NSPasteboardItem ] = [ ]
119+ for itemSnapshot in snapshot. items {
120+ let newItem = NSPasteboardItem ( )
121+ for (type, data) in itemSnapshot. dataByType {
122+ newItem. setData ( data, forType: type)
123+ }
124+ newItems. append ( newItem)
125+ }
126+
127+ pasteboard. writeObjects ( newItems)
128+ NSLog ( " [TextInjector] Restored clipboard with %ld items " , newItems. count)
129+ }
130+
66131 // MARK: - Paste Simulation
67132
68133 /// Simulate Cmd+V keystroke to paste from clipboard
0 commit comments