From d76d2f4b3af83185c312b13a4c530e4a077bc418 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Sun, 19 Jul 2026 11:46:31 -0700 Subject: [PATCH] Fix clipboard OSC 52 fallback: walk past nvim's tty-less embed process Nvim isn't a single process attached to the terminal: the process that actually spawns jobs (clipboard-copy included) is a separate `nvim --embed` core process with no tty of its own (ps shows `?`); the real pty belongs to a distinct parent UI process. Checking only the immediate parent's tty (as the previous fix did) found the embed process's `?` and fell through to the broken /dev/tty path every time in real interactive use, even though it looked fine in headless testing. Walk up the process tree past any tty-less ancestors instead. Verified against the real docker-compose path in actual (non-headless) TUI mode, with real keystrokes fed through a pty: the OSC 52 sequence now reaches the terminal with no error, where it previously still failed. --- bin/clipboard-copy | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/bin/clipboard-copy b/bin/clipboard-copy index 7a593aa..9368bfd 100755 --- a/bin/clipboard-copy +++ b/bin/clipboard-copy @@ -20,11 +20,24 @@ else data=$(base64 | tr -d '\n') # /dev/tty needs this process's own session to have a controlling # terminal, but a job spawned by nvim's clipboard provider doesn't get - # one (nvim's job-spawning doesn't establish a ctty for children). - # Resolve the real terminal from the parent's (nvim's) recorded tty - # instead, which doesn't depend on this process having its own ctty. - real_tty="/dev/$(ps -o tty= -p "$PPID" 2>/dev/null | tr -d ' ')" - if [ -w "$real_tty" ]; then + # one (nvim's job-spawning doesn't establish a ctty for children). Nvim + # itself also isn't a single process: the one actually spawning jobs is + # a `nvim --embed` core process with no tty of its own (shown as `?` in + # ps) -- the real terminal belongs to a separate parent UI process. Walk + # up the process tree past any tty-less ancestors to find it. + real_tty="" + pid="$PPID" + i=0 + while [ "$i" -lt 10 ] && [ -n "$pid" ] && [ "$pid" != "0" ]; do + t=$(ps -o tty= -p "$pid" 2>/dev/null | tr -d ' ') + if [ -n "$t" ] && [ "$t" != "?" ]; then + real_tty="/dev/$t" + break + fi + pid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ') + i=$((i + 1)) + done + if [ -n "$real_tty" ] && [ -w "$real_tty" ]; then printf '\033]52;c;%s\a' "$data" > "$real_tty" else printf '\033]52;c;%s\a' "$data" > /dev/tty