From 1289e8afd8cc5ac6cf9fbcb8f1a290aaded9bbbd Mon Sep 17 00:00:00 2001 From: "CJACK." Date: Thu, 2 Apr 2026 12:59:05 +0800 Subject: [PATCH] fix(webui): make API key copy action reliable --- webui/src/features/account/ApiKeysPanel.jsx | 71 ++++++++++++++++++--- webui/src/locales/en.json | 1 + webui/src/locales/zh.json | 1 + 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/webui/src/features/account/ApiKeysPanel.jsx b/webui/src/features/account/ApiKeysPanel.jsx index 301d826..956c0fa 100644 --- a/webui/src/features/account/ApiKeysPanel.jsx +++ b/webui/src/features/account/ApiKeysPanel.jsx @@ -1,6 +1,31 @@ +import { useState } from 'react' import { Check, ChevronDown, Copy, Plus, Trash2 } from 'lucide-react' import clsx from 'clsx' +function fallbackCopyText(text) { + const textArea = document.createElement('textarea') + textArea.value = text + textArea.setAttribute('readonly', '') + textArea.style.position = 'fixed' + textArea.style.top = '-9999px' + textArea.style.left = '-9999px' + + document.body.appendChild(textArea) + textArea.focus() + textArea.select() + + let copied = false + try { + copied = document.execCommand('copy') + } finally { + document.body.removeChild(textArea) + } + + if (!copied) { + throw new Error('copy failed') + } +} + export default function ApiKeysPanel({ t, config, @@ -11,6 +36,31 @@ export default function ApiKeysPanel({ setCopiedKey, onDeleteKey, }) { + const [failedKey, setFailedKey] = useState(null) + + const handleCopyKey = async (key) => { + try { + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(key) + } else { + fallbackCopyText(key) + } + setCopiedKey(key) + setFailedKey(null) + setTimeout(() => setCopiedKey(null), 2000) + } catch { + try { + fallbackCopyText(key) + setCopiedKey(key) + setFailedKey(null) + setTimeout(() => setCopiedKey(null), 2000) + } catch { + setFailedKey(key) + setTimeout(() => setFailedKey(null), 2500) + } + } + } + return (
(
-
+
+ {copiedKey === key && ( {t('accountManager.copied')} )} + {failedKey === key && ( + {t('accountManager.copyFailed')} + )}