Просмотр исходного кода

fix(ui): fallback to execCommand for clipboard copy when navigator.clipboard fails (#27993)

Co-authored-by: SpiritChen51 <spiritchen51@users.noreply.github.com>
SpiritChen51 3 месяцев назад
Родитель
Сommit
fe143df151
1 измененных файлов с 33 добавлено и 9 удалено
  1. 33 9
      packages/ui/src/components/message-part.tsx

+ 33 - 9
packages/ui/src/components/message-part.tsx

@@ -58,6 +58,27 @@ import { animate } from "motion"
 import { useLocation } from "@solidjs/router"
 import { useLocation } from "@solidjs/router"
 import { attached, inline, kind } from "./message-file"
 import { attached, inline, kind } from "./message-file"
 
 
+async function writeClipboard(text: string): Promise<boolean> {
+  const body = typeof document === "undefined" ? undefined : document.body
+  if (body) {
+    const textarea = document.createElement("textarea")
+    textarea.value = text
+    textarea.setAttribute("readonly", "")
+    textarea.style.position = "fixed"
+    textarea.style.opacity = "0"
+    textarea.style.pointerEvents = "none"
+    body.appendChild(textarea)
+    textarea.select()
+    const copied = document.execCommand("copy")
+    body.removeChild(textarea)
+    if (copied) return true
+  }
+
+  const clipboard = typeof navigator === "undefined" ? undefined : navigator.clipboard
+  if (!clipboard?.writeText) return false
+  return clipboard.writeText(text).then(() => true, () => false)
+}
+
 function ShellSubmessage(props: { text: string; animate?: boolean }) {
 function ShellSubmessage(props: { text: string; animate?: boolean }) {
   let widthRef: HTMLSpanElement | undefined
   let widthRef: HTMLSpanElement | undefined
   let valueRef: HTMLSpanElement | undefined
   let valueRef: HTMLSpanElement | undefined
@@ -1064,9 +1085,10 @@ export function UserMessageDisplay(props: { message: UserMessage; parts: PartTyp
   const handleCopy = async () => {
   const handleCopy = async () => {
     const content = text()
     const content = text()
     if (!content) return
     if (!content) return
-    await navigator.clipboard.writeText(content)
-    setState("copied", true)
-    setTimeout(() => setState("copied", false), 2000)
+    if (await writeClipboard(content)) {
+      setState("copied", true)
+      setTimeout(() => setState("copied", false), 2000)
+    }
   }
   }
 
 
   const revert = () => {
   const revert = () => {
@@ -1490,9 +1512,10 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
   const handleCopy = async () => {
   const handleCopy = async () => {
     const content = text()
     const content = text()
     if (!content) return
     if (!content) return
-    await navigator.clipboard.writeText(content)
-    setCopied(true)
-    setTimeout(() => setCopied(false), 2000)
+    if (await writeClipboard(content)) {
+      setCopied(true)
+      setTimeout(() => setCopied(false), 2000)
+    }
   }
   }
 
 
   return (
   return (
@@ -1834,9 +1857,10 @@ ToolRegistry.register({
     const handleCopy = async () => {
     const handleCopy = async () => {
       const content = text()
       const content = text()
       if (!content) return
       if (!content) return
-      await navigator.clipboard.writeText(content)
-      setCopied(true)
-      setTimeout(() => setCopied(false), 2000)
+      if (await writeClipboard(content)) {
+        setCopied(true)
+        setTimeout(() => setCopied(false), 2000)
+      }
     }
     }
 
 
     return (
     return (