/** * @file nativePaste — Settings → Appearance → "Paste images natively": a * per-browser preference in `localStorage` (`agenthub.terminal.nativePaste`, * deliberately NOT synced to the account — it is about this browser's * machine). On (the default), a `claude` pane in a local-mode dashboard opened * from this machine's own browser gets an image paste as `^V`, so `claude` * reads the desktop clipboard itself like a native terminal — nothing is * uploaded or copied. Off, the image is uploaded like a remote browser's. An * SSH tunnel's browser also reaches the dashboard by loopback, and its `^V` * would read the SERVER's clipboard — that is what turning it off is for. * The server must also be able to read its clipboard (`nativePasteWorks`: on * Linux `wl-paste`/`xclip` and a display) — else the `^V` pasted nothing. * On Windows Claude Code binds its image paste to Alt+V, not Ctrl+V * (`nativePasteKey`): a `^V` there was silently ignored. * @author Nguyễn Ngọc Trí Vĩ */ import { api } from "./api"; export const NATIVE_PASTE_KEY = "agenthub.terminal.nativePaste"; /** True unless this browser turned native image paste off. */ export function readNativePaste(): boolean { try { return window.localStorage.getItem(NATIVE_PASTE_KEY) !== "off"; } catch { return true; } } export function writeNativePaste(on: boolean) { try { if (on) window.localStorage.removeItem(NATIVE_PASTE_KEY); else window.localStorage.setItem(NATIVE_PASTE_KEY, "off"); } catch { /* storage blocked — the default */ } } let works: boolean | null = null; let asked: Promise | null = null; // Claude Code's image-paste chord on the dashboard's machine: Ctrl+V, but // Alt+V on Windows, where a ^V reaches no binding and pastes nothing. let key = "\x16"; /** Whether the dashboard's machine can hand a pane its clipboard, as last * answered — false until it has (an upload then, which always works). The * first call asks the server once per page. */ export function nativePasteWorks(): boolean { asked ??= api.run .nativePaste() .then((r) => { works = r.native === true; key = r.key === "alt+v" ? "\x1bv" : "\x16"; }) .catch(() => void (works = false)); return works === true; } /** The bytes that make the pane's `claude` read its clipboard: ^V, or ESC v * (Alt+V) on a Windows dashboard. */ export function nativePasteKey(): string { return key; } /** Test seam: forget the server's answer. */ export function __resetNativePaste() { works = null; asked = null; key = "\x16"; }