diff --git a/mateclaw-ui/src/components/skill/PreflightInstallDialog.vue b/mateclaw-ui/src/components/skill/PreflightInstallDialog.vue index 98519241..e1408a9e 100644 --- a/mateclaw-ui/src/components/skill/PreflightInstallDialog.vue +++ b/mateclaw-ui/src/components/skill/PreflightInstallDialog.vue @@ -79,6 +79,7 @@ import { computed, ref, watch } from 'vue' import { useI18n } from 'vue-i18n' import { ElMessage } from 'element-plus' import { skillApi } from '@/api/index' +import { copyToClipboard } from '@/utils/clipboard' interface RequirementStatus { key: string @@ -146,21 +147,7 @@ function handleClose() { async function copy(cmd: string) { try { - if (navigator.clipboard) { - await navigator.clipboard.writeText(cmd) - } else { - // Fallback for clipboard-API-disabled contexts (eg http://). The - // textarea trick is broadly supported and avoids a noisy permission - // failure on the production-ish workflow. - const ta = document.createElement('textarea') - ta.value = cmd - ta.style.position = 'fixed' - ta.style.left = '-9999px' - document.body.appendChild(ta) - ta.select() - document.execCommand('copy') - document.body.removeChild(ta) - } + await copyToClipboard(cmd) ElMessage.success(t('common.copied')) } catch { ElMessage.warning(t('common.copyFailed')) diff --git a/mateclaw-ui/src/utils/clipboard.ts b/mateclaw-ui/src/utils/clipboard.ts index c1c881cf..d7e31ae4 100644 --- a/mateclaw-ui/src/utils/clipboard.ts +++ b/mateclaw-ui/src/utils/clipboard.ts @@ -13,6 +13,7 @@ export async function copyToClipboard(text: string): Promise { ta.style.left = '-9999px' document.body.appendChild(ta) ta.select() - document.execCommand('copy') + const ok = document.execCommand('copy') document.body.removeChild(ta) + if (!ok) throw new Error('Clipboard copy command failed') }