diff --git a/macos/Sources/Ghostty/Surface View/OSSurfaceView.swift b/macos/Sources/Ghostty/Surface View/OSSurfaceView.swift index d07a2e0c8..3af562744 100644 --- a/macos/Sources/Ghostty/Surface View/OSSurfaceView.swift +++ b/macos/Sources/Ghostty/Surface View/OSSurfaceView.swift @@ -115,13 +115,39 @@ extension Ghostty { // MARK: Search State extension Ghostty.OSSurfaceView { - class SearchState: ObservableObject { + @MainActor class SearchState: ObservableObject { + /// The pasteboard used to persist the search needle. + /// + /// The `.find` pasteboard lets us sync our needle across the system and other find bars. + private let pasteboard: OSPasteboard + @Published var needle: String = "" @Published var selected: UInt? @Published var total: UInt? - init(from startSearch: Ghostty.Action.StartSearch) { - self.needle = startSearch.needle ?? "" + init( + from startSearch: Ghostty.Action.StartSearch, + pasteboard: OSPasteboard = OSPasteboard(name: .find) + ) { + self.pasteboard = pasteboard + if let needle = startSearch.needle, !needle.isEmpty { + self.needle = needle + writePasteboardNeedle() + } else { + readPasteboardNeedle() + } + } + + func readPasteboardNeedle() { + let pasteboardNeedle = pasteboard.string(forType: .string) + if let pasteboardNeedle, pasteboardNeedle != needle { + needle = pasteboardNeedle + } + } + + func writePasteboardNeedle() { + pasteboard.clearContents() + pasteboard.setString(needle, forType: .string) } } diff --git a/macos/Sources/Ghostty/Surface View/SurfaceView.swift b/macos/Sources/Ghostty/Surface View/SurfaceView.swift index 4b90a3016..52b39b074 100644 --- a/macos/Sources/Ghostty/Surface View/SurfaceView.swift +++ b/macos/Sources/Ghostty/Surface View/SurfaceView.swift @@ -401,6 +401,18 @@ extension Ghostty { .padding(.trailing, 8) } } + .onChange(of: searchState.needle) { _ in + searchState.writePasteboardNeedle() + } + .onReceive( + NotificationCenter.default.publisher( + for: OSApplication.didBecomeActiveNotification + ) + ) { _ in + // When the app becomes active, we want to check for external changes + // to our synced needle. + searchState.readPasteboardNeedle() + } #if canImport(AppKit) .onExitCommand { if searchState.needle.isEmpty { diff --git a/macos/Sources/Helpers/CrossKit.swift b/macos/Sources/Helpers/CrossKit.swift index 690e811bb..c7b782072 100644 --- a/macos/Sources/Helpers/CrossKit.swift +++ b/macos/Sources/Helpers/CrossKit.swift @@ -11,6 +11,7 @@ typealias OSView = NSView typealias OSColor = NSColor typealias OSSize = NSSize typealias OSPasteboard = NSPasteboard +typealias OSApplication = NSApplication protocol OSViewRepresentable: NSViewRepresentable where NSViewType == OSViewType { associatedtype OSViewType: NSView @@ -36,6 +37,7 @@ typealias OSView = UIView typealias OSColor = UIColor typealias OSSize = CGSize typealias OSPasteboard = UIPasteboard +typealias OSApplication = UIApplication protocol OSViewRepresentable: UIViewRepresentable { associatedtype OSViewType: UIView