From 6729ab8172ad755eaf0e42d9466db437a846398f Mon Sep 17 00:00:00 2001 From: monsterlag <64719697+monsterlag@users.noreply.github.com> Date: Thu, 22 Oct 2026 08:29:39 -0400 Subject: [PATCH] Fix Copy Path pasteboard contents so the path can be pasted into Terminal Copy Path declared a URL type without providing data and then appended a second pasteboard item, so apps reading the first item (Terminal, Pasteboard Viewer) found nothing to paste. Write one item carrying both the plain-text path and the file URL. Fixes #824 --- Xcodes/Backend/AppState.swift | 12 ++++++++---- XcodesTests/AppStateTests.swift | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index 6ac509bb..902172bf 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -825,12 +825,16 @@ class AppState: ObservableObject { } } - func copyPath(xcode: Xcode) { + func copyPath(xcode: Xcode, pasteboard: NSPasteboard = .general) { guard let installedXcodePath = xcode.installedPath else { return } - NSPasteboard.general.declareTypes([.URL, .string], owner: nil) - NSPasteboard.general.writeObjects([installedXcodePath.url as NSURL]) - NSPasteboard.general.setString(installedXcodePath.string, forType: .string) + // Write a single item with real data for each type. Declaring `.URL` without + // providing data left an empty first item that Terminal and other apps couldn't paste. + let item = NSPasteboardItem() + item.setString(installedXcodePath.url.absoluteString, forType: .fileURL) + item.setString(installedXcodePath.string, forType: .string) + pasteboard.clearContents() + pasteboard.writeObjects([item]) } func copyReleaseNote(from url: URL?) { diff --git a/XcodesTests/AppStateTests.swift b/XcodesTests/AppStateTests.swift index 2945b06c..c4594764 100644 --- a/XcodesTests/AppStateTests.swift +++ b/XcodesTests/AppStateTests.swift @@ -81,6 +81,21 @@ class AppStateTests: XCTestCase { subject = AppState() } + func test_CopyPath_WritesOneItemWithPathAndFileURL() throws { + let installedPath = try XCTUnwrap(Path("/Applications/Xcode 26.0.app")) + let xcode = Xcode(version: Version("26.0.0")!, installState: .installed(installedPath), selected: false, icon: nil) + let pasteboard = NSPasteboard(name: NSPasteboard.Name("AppStateTests-\(UUID().uuidString)")) + defer { pasteboard.releaseGlobally() } + + subject.copyPath(xcode: xcode, pasteboard: pasteboard) + + let items = try XCTUnwrap(pasteboard.pasteboardItems) + XCTAssertEqual(items.count, 1) + XCTAssertEqual(items.first?.types, [.fileURL, .string]) + XCTAssertEqual(items.first?.string(forType: .string), "/Applications/Xcode 26.0.app") + XCTAssertEqual(items.first?.string(forType: .fileURL), "file:///Applications/Xcode%2026.0.app") + } + func test_InstallError_Network401IsUnauthorized() { let error = NetworkError.non200StatusCode(statusCode: 401, data: Data())