Files
wled-screen-controller-mixed/server/src/wled_controller/static/sw.js
alexei.dolgolyov 2712c6682e Add EntitySelect/IconSelect UI improvements across modals
- Portal IconSelect popups to document.body with position:fixed to prevent
  clipping by modal overflow-y:auto
- Replace custom scene selectors in automation editor with EntitySelect
  command-palette pickers (main scene + fallback scene)
- Add IconSelect grid for automation deactivation mode (none/revert/fallback)
- Add IconSelect grid for automation condition type and match type
- Replace mapped zone source dropdowns with EntitySelect pickers
- Replace scene target selector with EntityPalette.pick() pattern
- Remove effect palette preview bar from CSS editor
- Remove sensitivity badge from audio color strip source cards
- Clean up unused scene-selector CSS and scene-target-add-row CSS
- Add locale keys for all new UI elements across en/ru/zh

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 16:00:30 +03:00

96 lines
3.3 KiB
JavaScript

/**
* Service Worker for LED Grab PWA.
*
* Strategy:
* - Static assets (/static/): stale-while-revalidate
* - API / config requests: network-only (device control must be live)
* - Navigation: network-first with offline fallback
*/
const CACHE_NAME = 'ledgrab-v33';
// Only pre-cache static assets (no auth required).
// Do NOT pre-cache '/' — it requires API key auth and would cache an error page.
const PRECACHE_URLS = [
'/static/css/base.css',
'/static/css/layout.css',
'/static/css/components.css',
'/static/css/cards.css',
'/static/css/modal.css',
'/static/css/calibration.css',
'/static/css/advanced-calibration.css',
'/static/css/dashboard.css',
'/static/css/streams.css',
'/static/css/patterns.css',
'/static/css/automations.css',
'/static/css/tutorials.css',
'/static/css/mobile.css',
'/static/icons/icon-192.png',
'/static/icons/icon-512.png',
];
// Install: pre-cache core shell
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => cache.addAll(PRECACHE_URLS))
.then(() => self.skipWaiting())
);
});
// Activate: clean old caches
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys()
.then((keys) => Promise.all(
keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k))
))
.then(() => self.clients.claim())
);
});
// Fetch handler
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
// API and config: always network (device control must be live)
if (url.pathname.startsWith('/api/') || url.pathname.startsWith('/config/')) {
return; // fall through to default network fetch
}
// Static assets: stale-while-revalidate
if (url.pathname.startsWith('/static/')) {
event.respondWith(
caches.open(CACHE_NAME).then((cache) =>
cache.match(event.request).then((cached) => {
const fetchPromise = fetch(event.request).then((response) => {
if (response.ok) {
cache.put(event.request, response.clone());
}
return response;
}).catch(() => cached);
return cached || fetchPromise;
})
)
);
return;
}
// Navigation: network-only (page requires auth, no useful offline fallback)
if (event.request.mode === 'navigate') {
event.respondWith(
fetch(event.request).catch(() =>
new Response(
'<html><body style="font-family:system-ui;text-align:center;padding:60px 20px;background:#1a1a1a;color:#ccc">' +
'<h2>LED Grab</h2><p>Cannot reach the server. Check that it is running and you are on the same network.</p>' +
'<button onclick="location.reload()" style="margin-top:20px;padding:10px 24px;border-radius:8px;border:none;background:#4CAF50;color:#fff;font-size:1rem;cursor:pointer">Retry</button>' +
'</body></html>',
{ status: 503, headers: { 'Content-Type': 'text/html' } }
)
)
);
return;
}
});