返回 DeepSeek-Reasonix
clipboard.ts
根目录 / desktop / frontend / src / lib / clipboard.ts
1 // Clipboard writes for the desktop shell: the async Clipboard API when the
2 // webview grants it, the Wails runtime bridge when it does not, and a hidden
3 // textarea + execCommand as the last resort.
4
5 export async function writeClipboardText(value: string): Promise<boolean> {
6 try {
7 if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
8 await navigator.clipboard.writeText(value);
9 return true;
10 }
11 } catch {
12 // Permission denied or unavailable — try the Wails bridge.
13 }
14 try {
15 if (typeof window !== "undefined" && (await window.runtime?.ClipboardSetText?.(value))) {
16 return true;
17 }
18 } catch {
19 // Bridge missing or failed — fall through to execCommand.
20 }
21 return fallbackCopyText(value);
22 }
23
24 // execCommand("copy") needs a selected editable element, so this selects a
25 // hidden textarea and must hand the user's selection and focus back afterwards.
26 export function fallbackCopyText(value: string): boolean {
27 const activeElement = document.activeElement;
28 const selection = document.getSelection();
29 const ranges: Range[] = [];
30 if (selection) {
31 for (let index = 0; index < selection.rangeCount; index += 1) {
32 ranges.push(selection.getRangeAt(index));
33 }
34 }
35 const textarea = document.createElement("textarea");
36 textarea.value = value;
37 textarea.setAttribute("readonly", "");
38 textarea.style.position = "fixed";
39 textarea.style.inset = "0 auto auto 0";
40 textarea.style.width = "1px";
41 textarea.style.height = "1px";
42 textarea.style.opacity = "0";
43 document.body.appendChild(textarea);
44 textarea.select();
45 let ok = false;
46 try {
47 ok = document.execCommand("copy");
48 } catch {
49 // Some WebViews reject execCommand("copy") with NotAllowedError instead of
50 // returning false; treat that as a failed copy, never a thrown rejection, so
51 // callers (and writeClipboardText's Promise<boolean> contract) stay honored.
52 ok = false;
53 } finally {
54 textarea.remove();
55 if (selection) {
56 selection.removeAllRanges();
57 for (const range of ranges) selection.addRange(range);
58 }
59 if (activeElement instanceof HTMLElement) activeElement.focus();
60 }
61 return ok;
62 }
63
63 lines TYPESCRIPT