mirror of
https://github.com/smittix/intercept.git
synced 2026-04-30 01:29:59 -07:00
fix: PWA install prompt - add PNG icons and fix apple-touch-icon
Browsers require PNG icons (192x192, 512x512) in the manifest to show the install prompt. SVG-only manifests are not sufficient. Also adds the 180x180 apple-touch-icon PNG for iOS home screen, bumps SW cache to v3, and adds scope to the manifest. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
BIN
static/icons/apple-touch-icon.png
Normal file
BIN
static/icons/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
BIN
static/icons/favicon-32.png
Normal file
BIN
static/icons/favicon-32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 750 B |
BIN
static/icons/icon-192.png
Normal file
BIN
static/icons/icon-192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
BIN
static/icons/icon-512.png
Normal file
BIN
static/icons/icon-512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -3,10 +3,21 @@
|
|||||||
"short_name": "INTERCEPT",
|
"short_name": "INTERCEPT",
|
||||||
"description": "Unified SIGINT platform for software-defined radio analysis",
|
"description": "Unified SIGINT platform for software-defined radio analysis",
|
||||||
"start_url": "/",
|
"start_url": "/",
|
||||||
|
"scope": "/",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#0b1118",
|
"background_color": "#0b1118",
|
||||||
"theme_color": "#0b1118",
|
"theme_color": "#0b1118",
|
||||||
"icons": [
|
"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",
|
"src": "/static/icons/icon.svg",
|
||||||
"sizes": "any",
|
"sizes": "any",
|
||||||
|
|||||||
106
static/sw.js
106
static/sw.js
@@ -1,22 +1,22 @@
|
|||||||
/* INTERCEPT Service Worker — cache-first static, network-only for API/SSE/WS */
|
/* INTERCEPT Service Worker — cache-first static, network-only for API/SSE/WS */
|
||||||
const CACHE_NAME = 'intercept-v2';
|
const CACHE_NAME = 'intercept-v3';
|
||||||
|
|
||||||
const NETWORK_ONLY_PREFIXES = [
|
const NETWORK_ONLY_PREFIXES = [
|
||||||
'/stream', '/ws/', '/api/', '/gps/', '/wifi/', '/bluetooth/',
|
'/stream', '/ws/', '/api/', '/gps/', '/wifi/', '/bluetooth/',
|
||||||
'/adsb/', '/ais/', '/acars/', '/aprs/', '/tscm/', '/satellite/',
|
'/adsb/', '/ais/', '/acars/', '/aprs/', '/tscm/', '/satellite/',
|
||||||
'/meshtastic/', '/bt_locate/', '/receiver/', '/sensor/', '/pager/',
|
'/meshtastic/', '/bt_locate/', '/receiver/', '/sensor/', '/pager/',
|
||||||
'/sstv/', '/weather-sat/', '/subghz/', '/rtlamr/', '/dsc/', '/vdl2/',
|
'/sstv/', '/weather-sat/', '/subghz/', '/rtlamr/', '/dsc/', '/vdl2/',
|
||||||
'/spy/', '/space-weather/', '/websdr/', '/analytics/', '/correlation/',
|
'/spy/', '/space-weather/', '/websdr/', '/analytics/', '/correlation/',
|
||||||
'/recordings/', '/controller/', '/ops/',
|
'/recordings/', '/controller/', '/ops/',
|
||||||
];
|
];
|
||||||
|
|
||||||
const STATIC_PREFIXES = [
|
const STATIC_PREFIXES = [
|
||||||
'/static/css/',
|
'/static/css/',
|
||||||
'/static/js/',
|
'/static/js/',
|
||||||
'/static/icons/',
|
'/static/icons/',
|
||||||
'/static/fonts/',
|
'/static/fonts/',
|
||||||
];
|
];
|
||||||
|
|
||||||
const CACHE_EXACT = ['/manifest.json'];
|
const CACHE_EXACT = ['/manifest.json'];
|
||||||
|
|
||||||
function isHttpRequest(req) {
|
function isHttpRequest(req) {
|
||||||
@@ -29,9 +29,9 @@ function isNetworkOnly(req) {
|
|||||||
const accept = req.headers.get('Accept') || '';
|
const accept = req.headers.get('Accept') || '';
|
||||||
if (accept.includes('text/event-stream')) return true;
|
if (accept.includes('text/event-stream')) return true;
|
||||||
const url = new URL(req.url);
|
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) {
|
function isStaticAsset(req) {
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
if (CACHE_EXACT.includes(url.pathname)) return true;
|
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' },
|
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.addEventListener('install', (e) => {
|
self.addEventListener('install', (e) => {
|
||||||
self.skipWaiting();
|
self.skipWaiting();
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener('activate', (e) => {
|
self.addEventListener('activate', (e) => {
|
||||||
e.waitUntil(
|
e.waitUntil(
|
||||||
caches.keys().then(keys =>
|
caches.keys().then(keys =>
|
||||||
Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
|
Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
|
||||||
).then(() => self.clients.claim())
|
).then(() => self.clients.claim())
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.addEventListener('fetch', (e) => {
|
self.addEventListener('fetch', (e) => {
|
||||||
const req = e.request;
|
const req = e.request;
|
||||||
|
|
||||||
@@ -90,18 +90,18 @@ self.addEventListener('fetch', (e) => {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache-first for static assets
|
// Cache-first for static assets
|
||||||
if (isStaticAsset(req)) {
|
if (isStaticAsset(req)) {
|
||||||
e.respondWith(
|
e.respondWith(
|
||||||
caches.open(CACHE_NAME).then(cache =>
|
caches.open(CACHE_NAME).then(cache =>
|
||||||
cache.match(req).then(cached => {
|
cache.match(req).then(cached => {
|
||||||
if (cached) {
|
if (cached) {
|
||||||
// Revalidate in background
|
// Revalidate in background
|
||||||
fetch(req).then(res => {
|
fetch(req).then(res => {
|
||||||
if (res && res.status === 200) cache.put(req, res.clone());
|
if (res && res.status === 200) cache.put(req, res.clone());
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
return fetch(req).then(res => {
|
return fetch(req).then(res => {
|
||||||
if (res && res.status === 200) cache.put(req, res.clone());
|
if (res && res.status === 200) cache.put(req, res.clone());
|
||||||
@@ -111,12 +111,12 @@ self.addEventListener('fetch', (e) => {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Network-first for HTML pages
|
// Network-first for HTML pages
|
||||||
e.respondWith(
|
e.respondWith(
|
||||||
fetch(req).catch(() =>
|
fetch(req).catch(() =>
|
||||||
caches.match(req).then(cached => cached || new Response('Offline', { status: 503 }))
|
caches.match(req).then(cached => cached || new Response('Offline', { status: 503 }))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<meta name="theme-color" content="#0b1118">
|
<meta name="theme-color" content="#0b1118">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<link rel="apple-touch-icon" href="/static/icons/icon.svg">
|
<link rel="apple-touch-icon" href="/static/icons/apple-touch-icon.png">
|
||||||
<!-- Disclaimer gate - must accept before seeing welcome page -->
|
<!-- Disclaimer gate - must accept before seeing welcome page -->
|
||||||
<script>
|
<script>
|
||||||
// Check BEFORE page renders - if disclaimer not accepted, hide welcome page
|
// Check BEFORE page renders - if disclaimer not accepted, hide welcome page
|
||||||
|
|||||||
Reference in New Issue
Block a user