Файли
openidea/src/features/project-tree/clipboard.ts
T
l0ng-ai 0458424f9c fix(project): expire stale file clipboard after OS replacement (#598)
Project Paste could reuse an earlier file copy after another app replaced the OS clipboard, and Cut files stayed dimmed. The tree now tracks the hand-off to the OS clipboard and rechecks it on window focus and just before Paste. A local Copy or Cut is cleared once the OS holds a different or empty file list; failed writes and clipboard read errors keep it. The native adapters now distinguish read failures from empty lists.

In remote windows, Copy and Cut write newline-joined server paths as plain text, tree Paste keeps the server source, and a later OS file list replaces the remote copy and imports those client files. A plain-text replacement stays undetectable there without a clipboard read that may prompt.

Verified on macOS, locally and against a remote host. The Windows clipboard code was not run.
2026-11-01 08:32:33 +08:00

54 рядки
1.5 KiB
TypeScript

/**
* The OS clipboard's file list: what Explorer copies, and what the project view
* puts there so Explorer can paste it back. The format is native (Windows
* `CF_HDROP`), so the backend owns it; a plain browser has nothing to exchange
* and every call degrades to "no files".
*/
import { call, commands, isTauri } from "@/platform/ipc";
export interface OsClipboard {
/** Files Explorer copied; empty when the clipboard holds none. */
files(): Promise<string[]>;
/** Whether the clipboard holds files: cheap, for the Paste item's enabled state. */
hasFiles(): Promise<boolean>;
/** True only when the OS accepted the file list; false for unsupported hosts. */
setFiles(paths: string[]): Promise<boolean>;
}
const native: OsClipboard = {
async files() {
if (!isTauri()) return [];
return call(commands.clipboardFileList());
},
async hasFiles() {
if (!isTauri()) return false;
try {
return await commands.clipboardHasFiles();
} catch {
return false;
}
},
async setFiles(paths) {
if (!isTauri() || !paths.length) return false;
try {
return await call(commands.clipboardSetFileList(paths));
} catch {
// The internal clipboard still works; a failed hand-off is not an error.
return false;
}
},
};
let current = native;
export function osClipboard(): OsClipboard {
return current;
}
/** Tests: force a clipboard implementation. */
export function setOsClipboardForTests(clipboard: OsClipboard | null) {
current = clipboard ?? native;
}