From 542602227353f29ff5379ff8bf86ff8b7f991cb5 Mon Sep 17 00:00:00 2001 From: bruno Date: Sat, 25 Jul 2026 10:32:39 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20HTTP=20fallback=20for=20clipboard=20?= =?UTF-8?q?=E2=80=94=20execCommand('copy')=20textarea=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added copyToClipboard() in utils.js with dual strategy: - HTTPS: navigator.clipboard.writeText() (modern API) - HTTP: textarea + execCommand('copy') (legacy fallback) - viewer.js: uses copyToClipboard() instead of direct navigator.clipboard - popout.html: same dual-strategy inline - Fixed: 'Erreur lors de la copie' on HTTP contexts --- frontend/js/utils.js | 32 +++++++++++++++++++++++++++++++- frontend/js/viewer.js | 8 ++++---- frontend/popout.html | 19 +++++++++++++++++-- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/frontend/js/utils.js b/frontend/js/utils.js index a5fb8f0..2b0ce1a 100644 --- a/frontend/js/utils.js +++ b/frontend/js/utils.js @@ -650,4 +650,34 @@ function initEditor() { ); } -export { getFileIcon, safeCreateIcons, flushIcons, safeHighlight, escapeHtml, EXT_ICONS, openEditor, closeEditor, saveFile, deleteFile, waitForCodeMirror, initEditor }; +function safeHighlight(block) { + try { hljs.highlightElement(block); } catch (e) { /* skip */ } +} + +/** Copy text to clipboard — works on both HTTP and HTTPS */ +function copyToClipboard(text) { + // Try modern API first (HTTPS-only) + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(text).catch(() => {}); + return true; + } + // Fallback for HTTP contexts + const ta = document.createElement("textarea"); + ta.value = text; + ta.style.position = "fixed"; + ta.style.left = "-9999px"; + ta.style.top = "-9999px"; + document.body.appendChild(ta); + ta.focus(); + ta.select(); + try { + document.execCommand("copy"); + return true; + } catch (e) { + return false; + } finally { + document.body.removeChild(ta); + } +} + +export { getFileIcon, safeCreateIcons, flushIcons, safeHighlight, escapeHtml, EXT_ICONS, openEditor, closeEditor, saveFile, deleteFile, waitForCodeMirror, initEditor, copyToClipboard }; diff --git a/frontend/js/viewer.js b/frontend/js/viewer.js index d802e2d..57b790f 100644 --- a/frontend/js/viewer.js +++ b/frontend/js/viewer.js @@ -681,16 +681,16 @@ export function renderFile(data) { wrapper.appendChild(pre); const btn = el("button", { class: "copy-code-btn", title: "Copier le code" }, [document.createTextNode("Copier")]); - btn.addEventListener("click", async (e) => { + btn.addEventListener("click", (e) => { e.stopPropagation(); const code = pre.querySelector("code")?.textContent || pre.textContent; - try { - await navigator.clipboard.writeText(code); + const copied = copyToClipboard(code); + if (copied) { btn.textContent = "✓ Copié"; btn.classList.add("copied"); if (typeof showToast === "function") showToast("Code copié dans le presse-papiers", "success"); setTimeout(() => { btn.textContent = "Copier"; btn.classList.remove("copied"); }, 2000); - } catch { + } else { if (typeof showToast === "function") showToast("Erreur lors de la copie", "error"); } }); diff --git a/frontend/popout.html b/frontend/popout.html index ea147a5..93d4017 100644 --- a/frontend/popout.html +++ b/frontend/popout.html @@ -913,11 +913,26 @@ const RightSidebarManager = { btn.addEventListener('click', function(e) { e.stopPropagation(); var code = (pre.querySelector('code') || pre).textContent; - navigator.clipboard.writeText(code).then(function() { + var ok = false; + // Try modern API first (HTTPS) + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(code).then(function() { ok = true; }).catch(function() {}); + } else { + // Fallback for HTTP + var ta = document.createElement('textarea'); + ta.value = code; + ta.style.position = 'fixed'; + ta.style.left = '-9999px'; + document.body.appendChild(ta); + ta.focus(); ta.select(); + try { document.execCommand('copy'); ok = true; } catch(er) {} + document.body.removeChild(ta); + } + if (ok) { btn.textContent = '✓ Copié'; btn.classList.add('copied'); setTimeout(function() { btn.textContent = 'Copier'; btn.classList.remove('copied'); }, 2000); - }).catch(function() {}); + } }); wrapper.appendChild(btn); });