From 9ebab211dd035e79cc16deac7f5b9077667a6e71 Mon Sep 17 00:00:00 2001 From: dekun Date: Tue, 16 Jun 2026 10:17:04 +0800 Subject: [PATCH] fix: make copy buttons work on HTTP panel pages Read share links from input fields and fall back to execCommand when Clipboard API is unavailable outside secure contexts. Co-authored-by: Cursor --- panel/static/app.js | 51 ++++++++++++++++++++++++++++++---- panel/static/style.css | 8 +++++- panel/templates/dashboard.html | 8 +++--- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/panel/static/app.js b/panel/static/app.js index 436bb2c..8c4080b 100644 --- a/panel/static/app.js +++ b/panel/static/app.js @@ -10,14 +10,55 @@ function apiUrl(path) { return `${base}${path}`; } -document.querySelectorAll("[data-copy]").forEach((btn) => { - btn.addEventListener("click", async () => { - const text = btn.dataset.copy; +function copyText(text) { + if (navigator.clipboard && window.isSecureContext) { + return navigator.clipboard.writeText(text); + } + return new Promise((resolve, reject) => { + const ta = document.createElement("textarea"); + ta.value = text; + ta.setAttribute("readonly", ""); + ta.style.position = "fixed"; + ta.style.left = "-9999px"; + document.body.appendChild(ta); + ta.select(); try { - await navigator.clipboard.writeText(text); + if (document.execCommand("copy")) { + resolve(); + } else { + reject(new Error("execCommand failed")); + } + } catch (err) { + reject(err); + } finally { + document.body.removeChild(ta); + } + }); +} + +document.querySelectorAll(".copy-row").forEach((row) => { + const input = row.querySelector(".copy-input"); + const btn = row.querySelector(".copy-btn"); + if (!input || !btn) return; + + input.addEventListener("click", () => { + input.select(); + input.setSelectionRange(0, input.value.length); + }); + + btn.addEventListener("click", async () => { + const text = input.value; + if (!text) { + toast("没有可复制的内容"); + return; + } + try { + input.select(); + input.setSelectionRange(0, text.length); + await copyText(text); toast("已复制到剪贴板"); } catch { - toast("复制失败,请手动选择文本"); + toast("复制失败,请选中上方链接后 Ctrl+C"); } }); }); diff --git a/panel/static/style.css b/panel/static/style.css index 5d4af75..6e4c74d 100644 --- a/panel/static/style.css +++ b/panel/static/style.css @@ -70,7 +70,8 @@ body { input[type="text"], input[type="password"], -input[readonly] { +input[readonly], +input.copy-input { width: 100%; padding: 10px 12px; border-radius: 8px; @@ -79,6 +80,11 @@ input[readonly] { color: var(--text); } +input.copy-input { + cursor: pointer; + font-size: 0.82rem; +} + .btn { border: 1px solid var(--border); background: #111827; diff --git a/panel/templates/dashboard.html b/panel/templates/dashboard.html index 9dec458..e66dd7f 100644 --- a/panel/templates/dashboard.html +++ b/panel/templates/dashboard.html @@ -75,15 +75,15 @@
- - + +
- - + +