import { execSync } from "node:child_process"; import type { ClipboardImage } from "@oh-my-pi/pi-natives"; import * as native from "@oh-my-pi/pi-natives"; const hasDisplay = process.platform !== "linux" || Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY); /** * Copy text to the system clipboard. * * Emits OSC 52 first when running in a real terminal (works over SSH/mosh), * then attempts native clipboard copy as best-effort for local sessions. * On Termux, tries `termux-clipboard-set` before native. * * @param text - UTF-8 text to place on the clipboard. */ export async function copyToClipboard(text: string): Promise { if (process.stdout.isTTY) { const onError = (err: unknown) => { process.stdout.off("error", onError); // Prevent unhandled 'error' from crashing the process when stdout is a closed pipe. if ((err as NodeJS.ErrnoException | null | undefined)?.code === "EPIPE") { return; } }; try { const encoded = Buffer.from(text).toString("base64"); const osc52 = `\x1b]52;c;${encoded}\x07`; process.stdout.on("error", onError); process.stdout.write(osc52, err => { process.stdout.off("error", onError); // If stdout is closed (e.g. piped to a process that exits early), // ignore EPIPE and proceed with native clipboard best-effort. if ((err as NodeJS.ErrnoException | null | undefined)?.code === "EPIPE") { return; } }); } catch (err) { process.stdout.off("error", onError); if ((err as NodeJS.ErrnoException | null | undefined)?.code !== "EPIPE") { // Ignore all write failures (OSC 52 is best-effort). } } } // Also try native tools (best effort for local sessions) try { if (process.env.TERMUX_VERSION) { try { execSync("termux-clipboard-set", { input: text, timeout: 5000 }); return; } catch { // Fall through to native } } await native.copyToClipboard(text); } catch { // Ignore — clipboard copy is best-effort } } /** * Read an image from the system clipboard. * * Returns null on Termux (no image clipboard support) or when no display * server is available (headless/SSH without forwarding). * * @returns PNG payload or null when no image is available. */ export async function readImageFromClipboard(): Promise { if (process.env.TERMUX_VERSION) { return null; } if (!hasDisplay) { return null; } return (await native.readImageFromClipboard()) ?? null; }