diff --git a/static/icons/apple-touch-icon.png b/static/icons/apple-touch-icon.png
new file mode 100644
index 0000000..de01f1f
Binary files /dev/null and b/static/icons/apple-touch-icon.png differ
diff --git a/static/icons/favicon-32.png b/static/icons/favicon-32.png
new file mode 100644
index 0000000..97da705
Binary files /dev/null and b/static/icons/favicon-32.png differ
diff --git a/static/icons/icon-192.png b/static/icons/icon-192.png
new file mode 100644
index 0000000..bff2b20
Binary files /dev/null and b/static/icons/icon-192.png differ
diff --git a/static/icons/icon-512.png b/static/icons/icon-512.png
new file mode 100644
index 0000000..0cdc634
Binary files /dev/null and b/static/icons/icon-512.png differ
diff --git a/static/manifest.json b/static/manifest.json
index 4a2eb81..aefdafa 100644
--- a/static/manifest.json
+++ b/static/manifest.json
@@ -3,10 +3,21 @@
"short_name": "INTERCEPT",
"description": "Unified SIGINT platform for software-defined radio analysis",
"start_url": "/",
+ "scope": "/",
"display": "standalone",
"background_color": "#0b1118",
"theme_color": "#0b1118",
"icons": [
+ {
+ "src": "/static/icons/icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "/static/icons/icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
{
"src": "/static/icons/icon.svg",
"sizes": "any",
diff --git a/static/sw.js b/static/sw.js
index af523e5..b270128 100644
--- a/static/sw.js
+++ b/static/sw.js
@@ -1,22 +1,22 @@
-/* INTERCEPT Service Worker — cache-first static, network-only for API/SSE/WS */
-const CACHE_NAME = 'intercept-v2';
-
-const NETWORK_ONLY_PREFIXES = [
- '/stream', '/ws/', '/api/', '/gps/', '/wifi/', '/bluetooth/',
- '/adsb/', '/ais/', '/acars/', '/aprs/', '/tscm/', '/satellite/',
- '/meshtastic/', '/bt_locate/', '/receiver/', '/sensor/', '/pager/',
- '/sstv/', '/weather-sat/', '/subghz/', '/rtlamr/', '/dsc/', '/vdl2/',
+/* INTERCEPT Service Worker — cache-first static, network-only for API/SSE/WS */
+const CACHE_NAME = 'intercept-v3';
+
+const NETWORK_ONLY_PREFIXES = [
+ '/stream', '/ws/', '/api/', '/gps/', '/wifi/', '/bluetooth/',
+ '/adsb/', '/ais/', '/acars/', '/aprs/', '/tscm/', '/satellite/',
+ '/meshtastic/', '/bt_locate/', '/receiver/', '/sensor/', '/pager/',
+ '/sstv/', '/weather-sat/', '/subghz/', '/rtlamr/', '/dsc/', '/vdl2/',
'/spy/', '/space-weather/', '/websdr/', '/analytics/', '/correlation/',
'/recordings/', '/controller/', '/ops/',
];
-
-const STATIC_PREFIXES = [
- '/static/css/',
- '/static/js/',
- '/static/icons/',
- '/static/fonts/',
-];
-
+
+const STATIC_PREFIXES = [
+ '/static/css/',
+ '/static/js/',
+ '/static/icons/',
+ '/static/fonts/',
+];
+
const CACHE_EXACT = ['/manifest.json'];
function isHttpRequest(req) {
@@ -29,9 +29,9 @@ function isNetworkOnly(req) {
const accept = req.headers.get('Accept') || '';
if (accept.includes('text/event-stream')) return true;
const url = new URL(req.url);
- return NETWORK_ONLY_PREFIXES.some(p => url.pathname.startsWith(p));
-}
-
+ return NETWORK_ONLY_PREFIXES.some(p => url.pathname.startsWith(p));
+}
+
function isStaticAsset(req) {
const url = new URL(req.url);
if (CACHE_EXACT.includes(url.pathname)) return true;
@@ -62,19 +62,19 @@ function fallbackResponse(req, status = 503) {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
}
-
-self.addEventListener('install', (e) => {
- self.skipWaiting();
-});
-
-self.addEventListener('activate', (e) => {
- e.waitUntil(
- caches.keys().then(keys =>
- Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
- ).then(() => self.clients.claim())
- );
-});
-
+
+self.addEventListener('install', (e) => {
+ self.skipWaiting();
+});
+
+self.addEventListener('activate', (e) => {
+ e.waitUntil(
+ caches.keys().then(keys =>
+ Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
+ ).then(() => self.clients.claim())
+ );
+});
+
self.addEventListener('fetch', (e) => {
const req = e.request;
@@ -90,18 +90,18 @@ self.addEventListener('fetch', (e) => {
);
return;
}
-
- // Cache-first for static assets
- if (isStaticAsset(req)) {
- e.respondWith(
- caches.open(CACHE_NAME).then(cache =>
- cache.match(req).then(cached => {
- if (cached) {
- // Revalidate in background
- fetch(req).then(res => {
- if (res && res.status === 200) cache.put(req, res.clone());
- }).catch(() => {});
- return cached;
+
+ // Cache-first for static assets
+ if (isStaticAsset(req)) {
+ e.respondWith(
+ caches.open(CACHE_NAME).then(cache =>
+ cache.match(req).then(cached => {
+ if (cached) {
+ // Revalidate in background
+ fetch(req).then(res => {
+ if (res && res.status === 200) cache.put(req, res.clone());
+ }).catch(() => {});
+ return cached;
}
return fetch(req).then(res => {
if (res && res.status === 200) cache.put(req, res.clone());
@@ -111,12 +111,12 @@ self.addEventListener('fetch', (e) => {
)
);
return;
- }
-
- // Network-first for HTML pages
- e.respondWith(
- fetch(req).catch(() =>
- caches.match(req).then(cached => cached || new Response('Offline', { status: 503 }))
- )
- );
-});
+ }
+
+ // Network-first for HTML pages
+ e.respondWith(
+ fetch(req).catch(() =>
+ caches.match(req).then(cached => cached || new Response('Offline', { status: 503 }))
+ )
+ );
+});
diff --git a/templates/index.html b/templates/index.html
index f3415b4..0ca2dfc 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -10,7 +10,7 @@
-
+