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