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