From 9695452974edefdb3adc10844d4c31fd8c07fabc Mon Sep 17 00:00:00 2001 From: Martin Dvorak Date: Thu, 24 Sep 2026 10:52:16 +0000 Subject: [PATCH] SMP join button: robust clipboard copy with fallback for insecure contexts --- src/app/components/apps.tsx | 41 +++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/app/components/apps.tsx b/src/app/components/apps.tsx index 48755a3..5d2b3c4 100644 --- a/src/app/components/apps.tsx +++ b/src/app/components/apps.tsx @@ -360,7 +360,7 @@ function SmpApp() { motd: null, icon: null, }); - const [copied, setCopied] = useState(false); + const [copyState, setCopyState] = useState<"idle" | "ok" | "fail">("idle"); useEffect(() => { let alive = true; @@ -389,13 +389,36 @@ function SmpApp() { const pending = info.status === "pending"; const copyAddress = async () => { + let ok = false; + // The async Clipboard API only exists in secure contexts (https / localhost) + // and can still reject (no focus, denied permission). try { - await navigator.clipboard.writeText(address); - setCopied(true); - window.setTimeout(() => setCopied(false), 1600); + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(address); + ok = true; + } } catch { - /* clipboard unavailable - the address is shown below anyway */ + ok = false; } + // Fallback that works in insecure contexts (e.g. LAN over http). + if (!ok) { + try { + const ta = document.createElement("textarea"); + ta.value = address; + ta.setAttribute("readonly", ""); + ta.style.position = "fixed"; + ta.style.top = "-1000px"; + ta.style.opacity = "0"; + document.body.appendChild(ta); + ta.select(); + ok = document.execCommand("copy"); + document.body.removeChild(ta); + } catch { + ok = false; + } + } + setCopyState(ok ? "ok" : "fail"); + window.setTimeout(() => setCopyState("idle"), 1800); }; return ( @@ -445,7 +468,7 @@ function SmpApp() {

- {copied + {copyState === "ok" ? "Paste it in Minecraft → Multiplayer → Add Server." - : "Join Server copies the address. Add it in Minecraft → Multiplayer → Add Server."} + : copyState === "fail" + ? `Copy manually: ${address}` + : "Join Server copies the address. Add it in Minecraft → Multiplayer → Add Server."}

);