From e3ad40a023ce27c3d26aed81c1d5797f17db7a91 Mon Sep 17 00:00:00 2001 From: MCHost Date: Tue, 24 Jun 2025 00:04:58 -0400 Subject: [PATCH] fix clipboard for the panel --- web/assets/index.js | 22 ++++++++++++++++++++-- web/index.html | 3 ++- web/worker.js | 43 +++++++++++++++++++++++++++++++------------ 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/web/assets/index.js b/web/assets/index.js index 3418f5b..4399cb9 100644 --- a/web/assets/index.js +++ b/web/assets/index.js @@ -1011,6 +1011,7 @@ const fileContextMenu = (elDisplay = null) => { .setLabel('Copy path') .setClickHandler(() => { const path = isNoneSelected ? activeConnection.path : selectedFiles[0].dataset.path; + console.log('Clipboard context:', window.location.href, window.top === window ? 'Parent' : 'Iframe'); navigator.clipboard.writeText(path); setStatus(`Copied path to clipboard`); }) @@ -2318,8 +2319,25 @@ btnShare.addEventListener('click', async() => { } else if (isMultiSelected) url = await getZipDownloadUrl(selected.map(el => el.dataset.path), activeConnection.path); if (url) { - navigator.clipboard.writeText(url); - setStatus(`Copied download link to clipboard`); + try { + await navigator.clipboard.writeText(url); + setStatus(`Copied download link to clipboard`); + } catch (err) { + console.error('Clipboard error:', err); + // Fallback + const textarea = document.createElement('textarea'); + textarea.value = url; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + setStatus(`Copied download link to clipboard (fallback)`); + } catch (fallbackErr) { + console.error('Fallback failed:', fallbackErr); + setStatus(`Failed to copy link. Please copy manually: ${url}`, true); + } + document.body.removeChild(textarea); + } } })) .addAction(action => action.setLabel('Cancel')) diff --git a/web/index.html b/web/index.html index 0a5152d..cf38abd 100644 --- a/web/index.html +++ b/web/index.html @@ -8,7 +8,8 @@ - + + diff --git a/web/worker.js b/web/worker.js index cbfcc4f..3ba4a98 100644 --- a/web/worker.js +++ b/web/worker.js @@ -1,4 +1,3 @@ - self.addEventListener('activate', (e) => { self.clients.claim(); }); @@ -6,24 +5,44 @@ self.addEventListener('activate', (e) => { self.addEventListener('fetch', (e) => { const reqUrl = e.request.url; e.respondWith((async() => { - // Open asset cache and see if this request is in it + // Open asset cache const cache = await caches.open('main'); const match = await caches.match(e.request); - // Request the resource from the network + + // Request from network const netRes = fetch(e.request).then((res) => { - // If the request was successful and this isn't an API call, - // update the cached resource + // Create a new response with updated headers + const newHeaders = new Headers(res.headers); + newHeaders.set('Permissions-Policy', 'clipboard-write=(self), clipboard-read=(self)'); + + const newRes = new Response(res.body, { + status: res.status, + statusText: res.statusText, + headers: newHeaders + }); + + // Cache non-API responses if (res.ok && !reqUrl.match(/\/api\/sftp\/.*$/)) { - cache.put(e.request, res.clone()); + cache.put(e.request, newRes.clone()); } - // Return the response - return res; + + return newRes; }).catch(e => { - console.error(e); - return match; + console.error('Fetch error:', e); + if (match) { + // Add header to cached response + const newHeaders = new Headers(match.headers); + newHeaders.set('Permissions-Policy', 'clipboard-write=(self), clipboard-read=(self)'); + return new Response(match.body, { + status: match.status, + statusText: match.statusText, + headers: newHeaders + }); + } + throw e; }); - // Return the cached resource if it exists - // Otherwise, return the network request + + // Return cached or network response return match || netRes; })()); }); \ No newline at end of file