Engineering

Copy any file from yazi to any app (c, s)

A 4-line config that puts the hovered file on the macOS system clipboard, so you can paste it into Slack, ChatGPT, Mail, Finder — anywhere that accepts file uploads.

yazi is a fast terminal file manager. Its default c menu copies the file path, directory, or filename as text — useful for shell work, but none of those let you drop the file into Slack or attach it to a ChatGPT message.

This adds one chord — c then s — that puts the actual file on the macOS system clipboard as a file reference. Cmd-V into Slack uploads it. Into Mail, it attaches. Into ChatGPT web, it becomes an image/PDF upload. Into Finder, it pastes as a file.

Why pbcopy isn't enough

pbcopy writes text to the clipboard. Pipe a file into it and the clipboard holds the file's contents, not a reference to the file. Slack's paste would dump your binary into a message as gibberish.

macOS apps that accept file uploads look for public.file-url on the pasteboard. The only reliable way to write that from a shell is AppleScript:

set the clipboard to POSIX file "/absolute/path/to/file"

That single line is the whole trick. Everything else is plumbing to hand yazi the hovered file path and call osascript.

The config

Two files. First, ~/.config/yazi/copy-file.sh:

#!/bin/bash
# Copy a file to the macOS system clipboard as a file reference.
# Paste into any app that accepts file uploads.
osascript -e "set the clipboard to POSIX file \"$1\""
chmod +x ~/.config/yazi/copy-file.sh

Then ~/.config/yazi/keymap.toml:

[[mgr.prepend_keymap]]
on   = ["c", "s"]
run  = 'shell -- ~/.config/yazi/copy-file.sh "%h"'
desc = "Copy file to system clipboard"

Restart yazi, hover any file, press c (yazi's copy menu appears, s → Copy file to system clipboard now sits with the built-in options), then s. File is on the clipboard. Switch to any app, Cmd-V.

What each piece does

Piece Role
mgr.prepend_keymap Adds a binding alongside yazi's defaults — c's path/dir/filename options still work
["c", "s"] A chord: press c, then s
shell -- Runs a command via sh -c; -- tells yazi "everything after this is raw, don't parse flags"
%h Yazi placeholder for the hovered file's absolute path
osascript -e Runs AppleScript; writes a public.file-url entry to the pasteboard

Gotchas I hit

Escaping the AppleScript string inside a TOML-single-quoted run value inside a yazi command template that dispatches to sh -c — four nested contexts — was ugly enough that I gave up on inlining the AppleScript. The wrapper script is the clean version.

A few dead ends:

Attempt Result
run = 'shell "pbcopy < $0"' Copies file content as text. Slack pastes gibberish.
run = 'shell -- osascript -e "... \"$0\""' $0 is deprecated in yazi 26; use %h.
run = 'shell -- osascript -e "... \"%h\""' Nested quote escaping breaks somewhere in the TOML → yazi → shell → osascript chain.
Wrapper script + shell -- script.sh "%h" Works.
Same, plus --block Works and errors become visible. Silent failures were the real killer while debugging.

Key takeaway: when a yazi binding silently does nothing, add --block to the shell call. It drops the terminal to a secondary screen and shows you stderr.

Verify it works

echo "hi" > /tmp/test.txt
# In yazi: hover /tmp/test.txt, press c, then s.
# In Slack (or ChatGPT, or Mail): Cmd-V.
# Your file uploads as an attachment.

If it doesn't work, temporarily add --block:

run = 'shell --block -- ~/.config/yazi/copy-file.sh "%h"'

Press the chord again — you'll see any error from osascript.

What this pattern unlocks

Once you have shell -- script.sh "%h" working, yazi becomes the sender for anything macOS can do from AppleScript: add to Reminders, open in a specific app, trigger a Shortcut, AirDrop, send to Messages. The file manager becomes a launchpad, not just a navigator.

TL;DR

  • Slack/ChatGPT/Mail pastes need a file reference, not text — only AppleScript writes those from a shell.
  • Put the AppleScript in a wrapper script so you don't fight quote escaping.
  • Use %h (not the deprecated $0) and shell -- in yazi 26+.
  • Add --block temporarily when a binding silently fails.
Stay updated

Get new articles in your inbox.

No spam, just technical articles and product updates.