diff --git a/copyparty/__init__.py b/copyparty/__init__.py
index 6430e655..23ec8eae 100644
--- a/copyparty/__init__.py
+++ b/copyparty/__init__.py
@@ -73,6 +73,7 @@ web/browser.js
web/browser2.html
web/cf.html
web/copyparty.gif
+web/copyparty.png
web/deps/busy.mp3
web/deps/easymde.css
web/deps/easymde.js
@@ -89,6 +90,7 @@ web/deps/sha512.ac.js
web/deps/sha512.hw.js
web/idp.html
web/iiam.gif
+web/manifest.json
web/md.css
web/md.html
web/md.js
@@ -110,6 +112,7 @@ web/splash.html
web/splash.js
web/svcs.html
web/svcs.js
+web/sw.js
web/tl/chi.js
web/tl/cze.js
web/tl/deu.js
diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css
index 045dab03..c19df1a5 100644
--- a/copyparty/web/browser.css
+++ b/copyparty/web/browser.css
@@ -679,7 +679,8 @@ html .ayjump:focus-visible {
#bbox-btns .x,
#qs_btns a .x {
line-height: .6em;
- vertical-align: center;
+ vertical-align: middle;
+ vertical-align: center;
}
#moresearch span {
font-size: .6em;
diff --git a/copyparty/web/browser.html b/copyparty/web/browser.html
index 6eda8026..c99d7178 100644
--- a/copyparty/web/browser.html
+++ b/copyparty/web/browser.html
@@ -7,6 +7,7 @@
+
{{ html_head }}
diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js
index 10b1cb27..a2807f3a 100644
--- a/copyparty/web/browser.js
+++ b/copyparty/web/browser.js
@@ -15,7 +15,8 @@ function loadScript(name, id) {
this.readyState === "loaded" || this.readyState === "complete") ) {
done = true;
- jsldp(id, name);
+ if(id)
+ jsldp(id, name);
// Handle memory leak in IE
s.onload = s.onreadystatechange = null;
@@ -32,6 +33,7 @@ function jsldp(a, b) {
}
loadScript('baguettebox', "J_BBX");
loadScript('up2k', "J_U2K");
+loadScript('sw');
// disables emojis
diff --git a/copyparty/web/copyparty.png b/copyparty/web/copyparty.png
new file mode 100644
index 00000000..8a17b717
Binary files /dev/null and b/copyparty/web/copyparty.png differ
diff --git a/copyparty/web/idp.html b/copyparty/web/idp.html
index 13360181..2ac37489 100644
--- a/copyparty/web/idp.html
+++ b/copyparty/web/idp.html
@@ -8,6 +8,7 @@
+
{{ html_head }}
diff --git a/copyparty/web/manifest.json b/copyparty/web/manifest.json
new file mode 100644
index 00000000..b68258ce
--- /dev/null
+++ b/copyparty/web/manifest.json
@@ -0,0 +1,33 @@
+{
+ "short_name": "copyparty",
+ "name": "copyparty",
+ "description": "the all in one file server",
+ "icons": [
+ {
+ "src": "/.cpr/copyparty.png",
+ "sizes": "512x512",
+ "type": "image/svg+xml"
+ }
+ ],
+ "start_url": "/?utm_medium=PWA&utm_source=launcher",
+ "display": "minimal-ui",
+ "orientation": "any",
+ "theme_color": "#fc5",
+ "background_color": "#222",
+ "share_target": {
+ "action": "/?utm_medium=PWA&utm_source=share-target&share-target",
+ "method": "POST",
+ "enctype": "multipart/form-data",
+ "params": {
+ "title": "name",
+ "text": "description",
+ "url": "link",
+ "files": [
+ {
+ "name": "files",
+ "accept": ["*/*"]
+ }
+ ]
+ }
+ }
+ }
\ No newline at end of file
diff --git a/copyparty/web/md.html b/copyparty/web/md.html
index e2d1a9e6..fa6e0bc5 100644
--- a/copyparty/web/md.html
+++ b/copyparty/web/md.html
@@ -7,6 +7,7 @@
+
{%- if edit %}
diff --git a/copyparty/web/mde.html b/copyparty/web/mde.html
index 042d1357..47bba37c 100644
--- a/copyparty/web/mde.html
+++ b/copyparty/web/mde.html
@@ -7,6 +7,7 @@
+
diff --git a/copyparty/web/msg.html b/copyparty/web/msg.html
index 30f53d55..891d0a1d 100644
--- a/copyparty/web/msg.html
+++ b/copyparty/web/msg.html
@@ -7,6 +7,7 @@
+
diff --git a/copyparty/web/sw.js b/copyparty/web/sw.js
new file mode 100644
index 00000000..7103a254
--- /dev/null
+++ b/copyparty/web/sw.js
@@ -0,0 +1,54 @@
+// service worker. required for PWAs
+// https://www.digitalapplied.com/blog/progressive-web-apps-2026-pwa-performance-guide
+// Register service worker on page load
+if ('serviceWorker' in navigator) {
+ self.addEventListener("fetch", (event) => {
+ // Regular requests not related to Web Share Target.
+ if (event.request.method !== "POST" || !event.request.enctype.has("share-target")) {
+ event.respondWith(fetch(event.request));
+ return;
+ }
+
+ // Requests related to Web Share Target.
+ event.respondWith(
+ (async () => {
+ const formData = await event.request.formData();
+ const link = formData.get("link") || "";
+ // Instead of the original URL `/save-bookmark/`, redirect
+ // the user to a URL returned by the `saveBookmark()`
+ // function, for example, `/`.
+ const responseUrl = await saveBookmark(link);
+ return Response.redirect(responseUrl, 303);
+ })(),
+ );
+ });
+ window.addEventListener('load', async () => {
+ try {
+ const registration = await navigator.serviceWorker.register(
+ '/service-worker.js',
+ { scope: '/' }
+ );
+ console.log('SW registered:', registration.scope);
+
+ // Check for waiting update
+ if (registration.waiting) {
+ notifyUserOfUpdate(registration);
+ }
+
+ // Listen for future updates
+ registration.addEventListener('updatefound', () => {
+ const newWorker = registration.installing;
+ newWorker?.addEventListener('statechange', () => {
+ if (
+ newWorker.state === 'installed' &&
+ navigator.serviceWorker.controller
+ ) {
+ notifyUserOfUpdate(registration);
+ }
+ });
+ });
+ } catch (error) {
+ console.error('SW registration failed:', error);
+ }
+ });
+}
\ No newline at end of file
diff --git a/scripts/sfx.ls b/scripts/sfx.ls
index 7578b96b..02355b36 100644
--- a/scripts/sfx.ls
+++ b/scripts/sfx.ls
@@ -75,6 +75,7 @@ copyparty/web/browser.js,
copyparty/web/browser2.html,
copyparty/web/cf.html,
copyparty/web/copyparty.gif,
+copyparty/web/copyparty.png,
copyparty/web/deps,
copyparty/web/deps/__init__.py,
copyparty/web/deps/busy.mp3,
@@ -93,6 +94,7 @@ copyparty/web/deps/sha512.ac.js,
copyparty/web/deps/sha512.hw.js,
copyparty/web/idp.html,
copyparty/web/iiam.gif,
+copyparty/web/manifest.json,
copyparty/web/md.css,
copyparty/web/md.html,
copyparty/web/md.js,
@@ -115,6 +117,7 @@ copyparty/web/splash.html,
copyparty/web/splash.js,
copyparty/web/svcs.html,
copyparty/web/svcs.js,
+copyparty/web/sw.js,
copyparty/web/tl,
copyparty/web/tl/chi.js,
copyparty/web/tl/cze.js,