From eccdfed32b6cfd3d7bb687e32f2c1de7665afb31 Mon Sep 17 00:00:00 2001 From: Sean Timm Date: Wed, 5 Nov 2025 17:35:52 -0700 Subject: [PATCH] gpui: Convert macOS clipboard file URLs to paths for paste (#36848) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - On macOS, pasting now inserts the actual file path when the clipboard contains a file URL (public.file-url/public.url) - Terminal paste remains text-only; no temp files or data URLs are created. If only raw image bytes exist on the clipboard, paste is a no-op. - Scope: macOS only; no dependency changes. - Added a test (test_file_url_converts_to_path) that verifies URL→path conversion using a unique pasteboard. Release Notes: - Improved pasting on macOS: now inserts the actual file path when the clipboard contains a file URL (enables image paste support for Claude Code) --- crates/gpui/src/platform/mac/platform.rs | 63 +++++++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 244350169caffef10ea2740a30e36772506e6145..4e0ee9f2c5773c014aa84ff0e69efb83cf5c54a7 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -1124,7 +1124,32 @@ impl Platform for MacPlatform { } } - // If it wasn't a string, try the various supported image types. + // Next, check for URL flavors (including file URLs). Some tools only provide a URL + // with no plain text entry. + { + // Try the modern UTType identifiers first. + let file_url_type: id = ns_string("public.file-url"); + let url_type: id = ns_string("public.url"); + + let url_data = if msg_send![types, containsObject: file_url_type] { + pasteboard.dataForType(file_url_type) + } else if msg_send![types, containsObject: url_type] { + pasteboard.dataForType(url_type) + } else { + nil + }; + + if url_data != nil && !url_data.bytes().is_null() { + let bytes = slice::from_raw_parts( + url_data.bytes() as *mut u8, + url_data.length() as usize, + ); + + return Some(self.read_string_from_clipboard(&state, bytes)); + } + } + + // If it wasn't a string or URL, try the various supported image types. for format in ImageFormat::iter() { if let Some(item) = try_clipboard_image(pasteboard, format) { return Some(item); @@ -1132,7 +1157,7 @@ impl Platform for MacPlatform { } } - // If it wasn't a string or a supported image type, give up. + // If it wasn't a string, URL, or a supported image type, give up. None } @@ -1707,6 +1732,40 @@ mod tests { ); } + #[test] + fn test_file_url_reads_as_url_string() { + let platform = build_platform(); + + // Create a file URL for an arbitrary test path and write it to the pasteboard. + // This path does not need to exist; we only validate URL→path conversion. + let mock_path = "/tmp/zed-clipboard-file-url-test"; + unsafe { + // Build an NSURL from the file path + let url: id = msg_send![class!(NSURL), fileURLWithPath: ns_string(mock_path)]; + let abs: id = msg_send![url, absoluteString]; + + // Encode the URL string as UTF-8 bytes + let len: usize = msg_send![abs, lengthOfBytesUsingEncoding: NSUTF8StringEncoding]; + let bytes_ptr = abs.UTF8String() as *const u8; + let data = NSData::dataWithBytes_length_(nil, bytes_ptr as *const c_void, len as u64); + + // Write as public.file-url to the unique pasteboard + let file_url_type: id = ns_string("public.file-url"); + platform + .0 + .lock() + .pasteboard + .setData_forType(data, file_url_type); + } + + // Ensure the clipboard read returns the URL string, not a converted path + let expected_url = format!("file://{}", mock_path); + assert_eq!( + platform.read_from_clipboard(), + Some(ClipboardItem::new_string(expected_url)) + ); + } + fn build_platform() -> MacPlatform { let platform = MacPlatform::new(false); platform.0.lock().pasteboard = unsafe { NSPasteboard::pasteboardWithUniqueName(nil) };