File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next"
55import { RiFileCopyLine } from "@remixicon/react"
66import { Button } from "@/components/ui/button"
77import { Input } from "@/components/ui/input"
8+ import { copyToClipboard } from "@/lib/clipboard"
89import { useMessage } from "@/lib/feedback/message"
910import { cn } from "@/lib/utils"
1011
@@ -29,8 +30,7 @@ export function CopyInput({
2930
3031 const handleCopy = async ( ) => {
3132 try {
32- if ( ! value ) throw new Error ( "No value to copy" )
33- await navigator . clipboard . writeText ( value )
33+ await copyToClipboard ( value )
3434 message . success ( t ( "Copy Success" ) )
3535 } catch {
3636 message . error ( t ( "Copy Failed" ) )
Original file line number Diff line number Diff line change 33import * as React from "react"
44import { useTranslation } from "react-i18next"
55import { Button } from "@/components/ui/button"
6+ import { copyToClipboard } from "@/lib/clipboard"
67import { RiFileCopyLine , RiCheckLine } from "@remixicon/react"
78
89interface Segment {
@@ -21,7 +22,7 @@ function useClipboard(value: string, copiedDuring = 3000) {
2122
2223 const copy = React . useCallback ( async ( ) => {
2324 try {
24- await navigator . clipboard . writeText ( value )
25+ await copyToClipboard ( value )
2526 setCopied ( true )
2627 setTimeout ( ( ) => setCopied ( false ) , copiedDuring )
2728 } catch {
Original file line number Diff line number Diff line change @@ -9,6 +9,7 @@ import { DataTable } from "@/components/data-table/data-table"
99import { useDataTable } from "@/hooks/use-data-table"
1010import { useObject } from "@/hooks/use-object"
1111import { useMessage } from "@/lib/feedback/message"
12+ import { copyToClipboard } from "@/lib/clipboard"
1213import { exportFile } from "@/lib/export-file"
1314import { getContentType } from "@/lib/mime-types"
1415import { formatBytes } from "@/lib/functions"
@@ -72,7 +73,7 @@ export function ObjectVersions({
7273 async ( versionId : string ) => {
7374 if ( ! versionId ) return
7475 try {
75- await navigator . clipboard . writeText ( versionId )
76+ await copyToClipboard ( versionId )
7677 message . success ( t ( "Copy Success" ) )
7778 } catch {
7879 message . error ( t ( "Copy Failed" ) )
Original file line number Diff line number Diff line change 1+ export async function copyToClipboard ( value : string ) : Promise < void > {
2+ if ( ! value ) throw new Error ( "No value to copy" )
3+
4+ if ( typeof navigator !== "undefined" && navigator . clipboard ?. writeText ) {
5+ try {
6+ await navigator . clipboard . writeText ( value )
7+ return
8+ } catch ( error ) {
9+ // Fallback to legacy copy only when Clipboard API is denied or unsupported in current context
10+ const fallback = legacyCopyToClipboard ( value )
11+ if ( fallback ) return
12+ if ( error instanceof Error ) throw error
13+ throw new Error ( "Failed to copy text" )
14+ }
15+ }
16+
17+ const fallback = legacyCopyToClipboard ( value )
18+ if ( fallback ) return
19+
20+ throw new Error ( "Failed to copy text" )
21+ }
22+
23+ function legacyCopyToClipboard ( value : string ) : boolean {
24+ if ( typeof document === "undefined" || ! document ?. execCommand ) return false
25+
26+ const textarea = document . createElement ( "textarea" )
27+ textarea . value = value
28+ textarea . setAttribute ( "readonly" , "" )
29+ textarea . style . position = "fixed"
30+ textarea . style . top = "0"
31+ textarea . style . left = "0"
32+ textarea . style . opacity = "0"
33+ textarea . style . pointerEvents = "none"
34+ textarea . style . zIndex = "-1"
35+
36+ document . body . appendChild ( textarea )
37+ textarea . focus ( )
38+ textarea . select ( )
39+
40+ let copied = false
41+ try {
42+ copied = document . execCommand ( "copy" )
43+ } catch {
44+ copied = false
45+ } finally {
46+ document . body . removeChild ( textarea )
47+ }
48+
49+ return copied
50+ }
You can’t perform that action at this time.
0 commit comments