Файли
openidea/src/platform/clipboard.ts
T
kkmihaiтаkkMihai 7af2b098cc
CI / Rust (ubuntu-22.04) (push) Failing after 1h0m17s
CI / Remote server (linux musl) (push) Failing after 25m8s
CI / Cargo deny (push) Successful in 1m39s
CI / Frontend (push) Successful in 25m40s
CI / E2E (push) Failing after 15m24s
CI / Rust (macos-latest) (push) Canceled after 0s
CI / Rust (windows-latest) (push) Canceled after 0s
feat(terminal): paste clipboard media as file paths (#880)
Pasting an image, audio or video from the clipboard into a local terminal saves the bytes to the per-user terminal cache (20 MB limit, files older than 24 h cleaned up on the next paste, private permissions) and inserts the shell-quoted path without running anything. Copied files paste their original paths, plain text pastes unchanged, and SSH sessions keep the existing remote image-paste route. Paths are quoted per shell (zsh/bash, fish, PowerShell, cmd.exe); fish quoting now escapes backslashes and single quotes.

Fixes #838

Co-authored-by: kkMihai <[email protected]>
2026-11-03 02:09:17 +08:00

52 рядки
1.7 KiB
TypeScript

import { call, commands, isTauri } from "./ipc";
import { ipcPath } from "./paths";
type FormatFiles = (paths: string[]) => string;
const rawPaths: FormatFiles = (paths) => paths.join("\n");
/** WebKit exposes native file URLs as Files, hiding the pasteboard's text flavor. */
export async function readClipboardText(
transfer?: DataTransfer | null,
formatFiles: FormatFiles = rawPaths,
): Promise<string> {
const text = transfer
? transfer.getData("text/plain")
: ((await navigator.clipboard?.readText().catch(() => "")) ?? "");
if ((text && formatFiles === rawPaths) || (transfer && !hasClipboardFiles(transfer)) || !isTauri()) return text;
let paths: string[];
try {
paths = await call(commands.clipboardFileList());
} catch {
return text;
}
// Windows also supplies the file list as text; unrelated text still wins.
if (text && text.replace(/\r\n/g, "\n").split("\n").map(ipcPath).join("\n") !== paths.join("\n")) return text;
return formatFiles(paths);
}
function hasClipboardFiles(transfer: DataTransfer): boolean {
return !!transfer.files.length || Array.from(transfer.types).includes("Files");
}
/** Claim synchronously so the native handler cannot emit an empty paste while IPC runs. */
export function pasteClipboardFiles(
event: ClipboardEvent,
paste: (text: string) => void,
formatFiles: FormatFiles = rawPaths,
): boolean {
const transfer = event.clipboardData;
if (
event.defaultPrevented ||
!transfer ||
(transfer.getData("text/plain") && formatFiles === rawPaths) ||
!hasClipboardFiles(transfer)
)
return false;
event.preventDefault();
event.stopImmediatePropagation();
void readClipboardText(transfer, formatFiles).then((text) => {
if (text) paste(text);
});
return true;
}