diff --git a/web/app/components/plugins/base/__tests__/key-value-item.spec.tsx b/web/app/components/plugins/base/__tests__/key-value-item.spec.tsx
index fd0964c90d..9c5ec03b7e 100644
--- a/web/app/components/plugins/base/__tests__/key-value-item.spec.tsx
+++ b/web/app/components/plugins/base/__tests__/key-value-item.spec.tsx
@@ -16,15 +16,16 @@ vi.mock('@/app/components/base/action-button', () => ({
),
}))
-const mockCopy = vi.fn()
-vi.mock('copy-to-clipboard', () => ({
- default: (...args: unknown[]) => mockCopy(...args),
+const mockWriteTextToClipboard = vi.fn()
+vi.mock('@/utils/clipboard', () => ({
+ writeTextToClipboard: (...args: unknown[]) => mockWriteTextToClipboard(...args),
}))
describe('KeyValueItem', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.useFakeTimers()
+ mockWriteTextToClipboard.mockResolvedValue(undefined)
})
afterEach(() => {
@@ -44,10 +45,10 @@ describe('KeyValueItem', () => {
expect(screen.queryByText('sk-secret')).not.toBeInTheDocument()
})
- it('copies actual value (not masked) when copy button is clicked', () => {
+ it('copies actual value (not masked) when copy button is clicked', async () => {
render()
fireEvent.click(screen.getByTestId('action-button'))
- expect(mockCopy).toHaveBeenCalledWith('sk-secret')
+ expect(mockWriteTextToClipboard).toHaveBeenCalledWith('sk-secret')
})
it('renders copy tooltip', () => {
diff --git a/web/app/components/plugins/base/key-value-item.tsx b/web/app/components/plugins/base/key-value-item.tsx
index a2a3459b5d..442470bb98 100644
--- a/web/app/components/plugins/base/key-value-item.tsx
+++ b/web/app/components/plugins/base/key-value-item.tsx
@@ -2,11 +2,11 @@
import type { FC } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
-import copy from 'copy-to-clipboard'
import * as React from 'react'
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
+import { writeTextToClipboard } from '@/utils/clipboard'
import { CopyCheck } from '../../base/icons/src/vender/line/files'
type Props = {
@@ -27,8 +27,9 @@ const KeyValueItem: FC = ({
const { t } = useTranslation()
const [isCopied, setIsCopied] = useState(false)
const handleCopy = useCallback(() => {
- copy(value)
- setIsCopied(true)
+ void writeTextToClipboard(value).then(() => {
+ setIsCopied(true)
+ })
}, [value])
useEffect(() => {
diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx
index 1dab8bdf84..d1e3294a75 100644
--- a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx
@@ -9,6 +9,7 @@ const mockDisableEndpoint = vi.fn()
const mockDeleteEndpoint = vi.fn()
const mockUpdateEndpoint = vi.fn()
const mockToastNotify = vi.fn()
+const mockWriteTextToClipboard = vi.fn()
vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(
@@ -72,6 +73,10 @@ vi.mock('@/service/use-endpoints', () => ({
}),
}))
+vi.mock('@/utils/clipboard', () => ({
+ writeTextToClipboard: (...args: unknown[]) => mockWriteTextToClipboard(...args),
+}))
+
vi.mock('@/app/components/header/indicator', () => ({
default: ({ color }: { color: string }) => ,
}))
@@ -136,6 +141,7 @@ const mockPluginDetail: PluginDetail = {
describe('EndpointCard', () => {
beforeEach(() => {
vi.clearAllMocks()
+ mockWriteTextToClipboard.mockResolvedValue(undefined)
// Reset failure flags
failureFlags.enable = false
failureFlags.disable = false
@@ -235,6 +241,14 @@ describe('EndpointCard', () => {
expect(screen.getByTestId('endpoint-modal'))!.toBeInTheDocument()
})
+ it('should copy endpoint url when copy action is clicked', () => {
+ render()
+
+ fireEvent.click(screen.getByRole('button', { name: 'common.operation.copy' }))
+
+ expect(mockWriteTextToClipboard).toHaveBeenCalledWith('https://api.example.com/api/test')
+ })
+
it('should call updateEndpoint when save in modal', () => {
render()
diff --git a/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx b/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx
index 9aa944c4b3..0378504419 100644
--- a/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx
@@ -12,7 +12,6 @@ import { Switch } from '@langgenius/dify-ui/switch'
import { toast } from '@langgenius/dify-ui/toast'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useBoolean } from 'ahooks'
-import copy from 'copy-to-clipboard'
import * as React from 'react'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -26,6 +25,7 @@ import {
useEnableEndpoint,
useUpdateEndpoint,
} from '@/service/use-endpoints'
+import { writeTextToClipboard } from '@/utils/clipboard'
import EndpointModal from './endpoint-modal'
import { NAME_FIELD } from './utils'
@@ -127,8 +127,9 @@ const EndpointCard = ({
const [isCopied, setIsCopied] = useState(false)
const handleCopy = (value: string) => {
- copy(value)
- setIsCopied(true)
+ void writeTextToClipboard(value).then(() => {
+ setIsCopied(true)
+ })
}
const handleDisableConfirmOpenChange = (open: boolean) => {