import AppKit // Capture reads the current selection by simulating Cmd+C, watching the // pasteboard change count, then restoring the previous clipboard contents so // the user's own clipboard is untouched. Requires Accessibility permission // for the synthesized events to be delivered. enum Capture { static func captureSelectionIntoStore() { let sourceApp = NSWorkspace.shared.frontmostApplication?.localizedName guard let text = captureSelection() else { return } let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return } Task { @MainActor in Store.shared.addText(text, sourceApp: sourceApp) PanelController.shared.show() } } static func captureSelection() -> String? { let pasteboard = NSPasteboard.general let backup = backupPasteboard(pasteboard) let oldChangeCount = pasteboard.changeCount simulateCopy() let deadline = Date().addingTimeInterval(0.5) var result: String? while Date() < deadline { if pasteboard.changeCount != oldChangeCount { result = pasteboard.string(forType: .string) break } usleep(10_000) } restorePasteboard(pasteboard, backup: backup) return result } private static func simulateCopy() { let source = CGEventSource(stateID: .hidSystemState) let keyCodeC: CGKeyCode = 8 // kVK_ANSI_C let keyDown = CGEvent(keyboardEventSource: source, virtualKey: keyCodeC, keyDown: true) keyDown?.flags = .maskCommand keyDown?.post(tap: .cghidEventTap) let keyUp = CGEvent(keyboardEventSource: source, virtualKey: keyCodeC, keyDown: false) keyUp?.flags = .maskCommand keyUp?.post(tap: .cghidEventTap) } private static func backupPasteboard(_ pasteboard: NSPasteboard) -> [[String: Data]] { var backup: [[String: Data]] = [] for item in pasteboard.pasteboardItems ?? [] { var entry: [String: Data] = [:] for type in item.types { if let data = item.data(forType: type) { entry[type.rawValue] = data } } backup.append(entry) } return backup } private static func restorePasteboard(_ pasteboard: NSPasteboard, backup: [[String: Data]]) { pasteboard.clearContents() let items = backup.map { entry -> NSPasteboardItem in let item = NSPasteboardItem() for (type, data) in entry { item.setData(data, forType: NSPasteboard.PasteboardType(type)) } return item } if !items.isEmpty { pasteboard.writeObjects(items) } } }