Jump to content

How to put files into the user's clipboard by using code?


Recommended Posts

Posted

Hey guys, I realize this may be a somewhat basic question. I have tried Google, searched extensively, and asked AI, but can't seem to find a reliable way to use code or a script to grab file paths and put them on the user's clipboard so that these files that are being referenced can be pasted in other apps.

 

I have Pasteflow, a paste stack workflow I created for Alfred, and I'm trying to make it compatible with files so users can save paths and then, with a click, put them back onto their clipboard to paste anywhere. Thanks to this I've been able to get paths to the files on Alfred's clipboard history. It's just the putting back on user's clipboard that is giving me trouble.

 

I've tried many approaches. Applescript set the clipboard to (POSIX file

"file path"... ... I've also tried set the clipboard to "file path" as «class furl»

 

 I've gotten pretty close, and that is why I find this a bit more frustrating. So far I have the following as part of my code which I run inside a script block in Alfred:

 

if (paths.length > 0) {

        // Get the pasteboard

        const pasteboard = $.NSPasteboard.generalPasteboard;

        

        // Clear the pasteboard

        pasteboard.clearContents;

        

        // Create array of file URLs

        const nsArray = $.NSMutableArray.alloc.init;

        paths.forEach(path => {

            const url = $.NSURL.fileURLWithPath(path);

            nsArray.addObject(url);

        });

        

        // Write to pasteboard

        pasteboard.writeObjects(nsArray);

...more code

 

 

With this, I can paste in Finder without issues, but again, pasting inside apps doesn't work. I can't paste on Bear (which allows me to copy from finder and it'll paste as an attachment). I can't paste in Pixelmator, which receives images I copy from Finder, again, and they would be pasted as images. In the mail app, I can paste, it just shows a ? as if the file didn't exist.

 

I understand this has to do with file type and how applications receive or interpret the content. However, Alfred does it perfectly with its clipboard. I can go back in time in Alfred's clipboard history and paste any image or file without issues anywhere, which is what I'm trying to achieve. It makes me believe that it's possible.

 

My coding/scripting ability is super limited. I've gone as far as trying to use AI to write me some swift code but again, I'm just stuck in the same place (can only paste on Finder, never on apps, not reliably like Alfred's clipboard history).

 

Any help is appreciated.

Posted

Possibly related:

Check out the Run Script object.

 

import AppKit

func copy(_ filePaths: ArraySlice<String>) {
    let pasteboard: NSPasteboard = .general
    let urls = filePaths.map { URL(fileURLWithPath: $0) }
    pasteboard.clearContents()
    pasteboard.writeObjects(urls as [NSPasteboardWriting])
}


let files: [String] = CommandLine.arguments
guard files.count > 1 else {
	FileHandle.standardError.write(Data("No input provided.".utf8))
	exit(1)
}

copy(files[1...])

 

Posted

Thank you very much @zeitlings for sending me in this direction. I've gone ahead and downloaded the sample workflow you shared in the linked discussion. I've been testing a lot. No clue why, but it's not working on my side. I put the file on the buffer, I assign a hotkey to copy it to my clipboard, but when I press the Hotkey several seconds pass until I hear the "pop" sound effect at the end. Nothing is copied. When I check the debugging messages I see lots of errors in there, like:

 

 

 error: failed to build module 'AppKit'; this SDK is not supported by the compiler (the SDK is built with 'Apple Swift version 6.0 effective-5.10 (swiftlang-6.0.0.7.43 clang-1600.0.25.3)', while this compiler is 'Apple Swift version 6.0.2 effective-5.10 (swiftlang-6.0.2.1.2 clang-1600.0.26.4)'). Please select a toolchain which matches the SDK.
 1 | import AppKit
   |        `- error: failed to build module 'AppKit'; this SDK is not supported by the compiler (the SDK is built with 'Apple Swift version 6.0 effective-5.10 (swiftlang-6.0.0.7.43 clang-1600.0.25.3)', while this compiler is 'Apple Swift version 6.0.2 effective-5.10 (swiftlang-6.0.2.1.2 clang-1600.0.26.4)'). Please select a toolchain which matches the SDK.
 2 | 
 3 | func copy(_ filePaths: ArraySlice<String>){
[17:45:38.796] File or File Buffer to Clipboard[Run Script] Processing complete
[17:45:38.798] File or File Buffer to Clipboard[Run Script] Passing output '' to Play Sound

 

(there's actually a lot more errors along the same lines). 

 

If this is working on your side then I wonder if I got a faulty install of Alfred or am I missing some dependencies or something. 

Posted

As an update, I think I've gotten pretty close and maybe this is what I'll end up incorporating in my Alfred Workflow, but it feels like it's not quite there yet. This works great for images. But for all other file types, depending be where will the user be pasting, it is mixed results.  

 

import Foundation
import AppKit

struct CopyFilesToClipboard {
    static func main() {
        let arguments = CommandLine.arguments
        guard arguments.count > 1 else {
            fputs("No input provided.\n", stderr)
            exit(1)
        }
        
        let pasteboard = NSPasteboard.general
        pasteboard.clearContents()
        
        // Convert paths to URLs
        let urls = arguments[1...].map { URL(fileURLWithPath: $0) }
        let nsurls = urls.map { $0 as NSURL }
        
        // Handle images specially
        let imageURLs = urls.filter { url in
            let ext = url.pathExtension.lowercased()
            return ["jpg", "jpeg", "png", "gif", "tiff", "bmp", "heic"].contains(ext)
        }
        
        if !imageURLs.isEmpty {
            // For images, load and write the actual image data
            for imageURL in imageURLs {
                if let imageData = try? Data(contentsOf: imageURL) {
                    // Get UTType based on file extension
                    let ext = imageURL.pathExtension.lowercased()
                    let uti = switch ext {
                        case "jpg", "jpeg": "public.jpeg"
                        case "png": "public.png" 
                        case "gif": "com.compuserve.gif"
                        case "tiff": "public.tiff"
                        case "bmp": "com.microsoft.bmp"
                        case "heic": "public.heic"
                        default: "public.image"
                    }
                    
                    pasteboard.setData(imageData, forType: NSPasteboard.PasteboardType(uti))
                }
            }
        }
        
        // Write all files as URLs too
        pasteboard.writeObjects(nsurls as [NSPasteboardWriting])
    }
}

CopyFilesToClipboard.main()

 

I checked out the "Rich Snippets" workflow and noticed that not only can it save text, but also files. Files that can be put back in the user's clipboard to paste in with much more compatibility that what I've managed with my code. However, the way it does it is by saving a "Binary Blob" that can then be put back on the user's clipboard. What I've now been trying to do is write a code that will read a file (give the file path) in a similar way... but no success until now.

 

Didn't think it would be this complicated.

Posted
29 minutes ago, gloogloo said:

However, the way it does it is by saving a "Binary Blob" that can then be put back on the user's clipboard.

 

The reason for that is to save every type representation inside the clipboard, since there isn’t just one.

 

On 12/1/2024 at 1:30 PM, zeitlings said:

Check out the Run Script object.

 

I tried that but was unable to e.g. paste workflows as attachments in Messages. That’s something I bumped into myself some time ago. It pastes file://path/here. I don’t recall exactly how I figured this out (I think I just decided to try it after exhausting everything else) but I only got consistent results after copying from an app. I.e. it didn’t work if the source of the code came from a script or executable, only from an app, which sounds exactly like the type of restriction Apple would add, intentionally or not.

 

They have insane and undocumented restrictions. For example, I have a local workflow to send messages (close to be ready to publish and it’s working well so far, though no ETA as other things are prioritary) and found through a ton of experimentation and research that you can really only send attachments to it via AppleScript if the file is in ~/Pictures. Wait, what? Yeah, exactly.

 

35 minutes ago, gloogloo said:

I checked out the "Rich Snippets" workflow

 

Check instead Workflow Actions. There’s a CopyFile.app in there. You call it like /usr/bin/open ./CopyFile.app --args "/one/path/here" "/another/path/here". I should eventually make it into an Automation Task, but I’d first like to be more certain it works reliably. Seems like you might have an interest in confirming that thoroughly!

 

You can find the JXA that compiles to that app in the repo in case you want to try to port it to Swift or something. I’d be interested in seeing if someone gets a better solution. At the time my priority was making it work reliably for that workflow (hence why it only exists in there) as it’s a tiny feature for which there’s a good chance I’m the only person who actively cares for.

Posted

Thank you @vitor wow, I got it working with CopyFile.app. I can't believe this. I spent so many hours researching, I thought I had tried everything. It seems like this is one of the biggest secrets ever and it seems like it's all related to that "undocumented restriction" you found. Thank you so much, really. I'll compile it myself in a similar way (I don't think I need it necessarily in swift), I've tested with many files and seems to be working as expected, but if I run into any issues or figure out it doesn't work for something specific I'll make sure to report back. Would definitely be awesome to have this as an automation task.

Posted
54 minutes ago, gloogloo said:

I'll compile it myself in a similar way (I don't think I need it necessarily in swift)

 

Unless you have an Apple developer account or are not planning to share the workflow, you can just copy the app. It’s signed and notarised.

 

56 minutes ago, gloogloo said:

I spent so many hours researching

 

They weren’t wasted, I bet. At the very least, it’s only because you mentioned Rich Snippets that I remembered to take a look at how I’d done it which in turn reminded me of Workflow Actions.

 

59 minutes ago, gloogloo said:

I've tested with many files and seems to be working as expected, but if I run into any issues or figure out it doesn't work for something specific I'll make sure to report back.

 

Thank you, please do.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...