From 9e41062555315dd5a9e8d00c49a42680e903ee41 Mon Sep 17 00:00:00 2001 From: hgosalia Date: Mon, 1 Jun 2026 21:19:01 -0400 Subject: [PATCH 1/2] Feature: 3D satellite, category search and service worker updates --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6528771..a00b7d7 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ | πŸ—“ Timeline | Chronological photo browser | | πŸ–Ό Lightbox | Full-size viewer with navigation and camera info | | πŸ“ Notes | Add notes to any pin | -| πŸ›° Map styles | Light, Bright, Dark, Terrain, 3D Terrain, Satellite, Globe | +| πŸ›° Map styles | Light, Bright, Dark, Terrain, 3D Terrain, Satellite, 3D Satellite, Globe | | πŸ”„ Clustering | Pins cluster by zoom, expand on click | | πŸ’Ύ Auto-save | Background backup to disk via `serve.py` | | πŸ“¦ Export / Import | Full dataset as compressed `.json.gz` | From 481c2c4e826251c2c32604ca810b51409ad5d1a9 Mon Sep 17 00:00:00 2001 From: hgosalia Date: Mon, 1 Jun 2026 21:21:35 -0400 Subject: [PATCH 2/2] Feature: 3D satellite, category search and service worker updates --- css/styles.css | 9 +++++ index.html | 10 +++++ js/data.js | 5 +-- js/map.js | 38 +++++++++--------- js/search.js | 104 +++++++++++++++++++++++++++++++++++++++++++++++-- serve.py | 26 ++++++++++++- sw.js | 15 ++----- 7 files changed, 169 insertions(+), 38 deletions(-) diff --git a/css/styles.css b/css/styles.css index 5195b6d..95c7c36 100644 --- a/css/styles.css +++ b/css/styles.css @@ -225,6 +225,15 @@ input,textarea,select{font-family:var(--font)} .dest-item-name{font-weight:500;color:var(--text)} .dest-item-detail{font-size:.66rem;color:var(--muted);margin-top:1px} #dest-loading{padding:10px 12px;font-size:.73rem;color:var(--muted);display:none} +#search-cat-wrap{position:relative} +#search-cat-btn{background:var(--surface2);border:1px solid var(--border);color:var(--muted);font-size:.73rem;font-weight:500;padding:5px 10px;border-radius:20px;cursor:pointer;white-space:nowrap;transition:all .15s;line-height:1} +#search-cat-btn:hover{background:var(--surface3);color:var(--text)} +#search-cat-btn.active{border-color:var(--accent);color:var(--accent)} +#search-cat-menu{position:absolute;top:calc(100% + 6px);right:0;background:var(--surface);border:1px solid var(--border);border-radius:10px;box-shadow:var(--shadow);display:none;z-index:100;min-width:130px;overflow:hidden} +#search-cat-menu.open{display:block} +.search-cat-item{padding:8px 14px;font-size:.73rem;cursor:pointer;transition:background .12s;color:var(--text)} +.search-cat-item:hover{background:var(--surface2)} +.search-cat-item.active{color:var(--accent);font-weight:500} /* PIN MARKERS (canvas-rendered via symbol layer β€” no DOM positioning) */ /* Cluster markers still use DOM for click expand */ diff --git a/index.html b/index.html index 81d99c1..a336c84 100644 --- a/index.html +++ b/index.html @@ -120,6 +120,15 @@
Searching…
+
+ +
+
All
+
🏨 Hotel
+
🍽 Restaurant
+
πŸ› Attraction
+
+
@@ -131,6 +140,7 @@
Terrain
3D Terrain
Satellite
+
3D Satellite
Globe
diff --git a/js/data.js b/js/data.js index 6ccb80c..d96f35c 100644 --- a/js/data.js +++ b/js/data.js @@ -405,7 +405,7 @@ async function autoSave() { if (!_autoSaveAvailable) return; try { // Upload new photo files that haven't been saved yet - const uploads = photos.filter(p => !_savedPhotoDisk.has(p.id) && p.dataUrl && p.dataUrl.startsWith('data:')); + const uploads = photos.filter(p => !_savedPhotoDisk.has(p.id) && !p.isEmptyPin); await Promise.all(uploads.map(p => uploadPhotoFile(p))); // Build metadata-only payload with file paths instead of base64 @@ -704,9 +704,6 @@ window.addEventListener('offline', () => updateOfflineState(true)); const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); if ('serviceWorker' in navigator && !isSafari) { navigator.serviceWorker.register('/sw.js', { updateViaCache: 'none' }).catch(err => console.warn('SW registration failed:', err)); - navigator.serviceWorker.ready.then(reg => { - if (reg.active) reg.active.postMessage({ type: 'set-port', port: location.port || '8765' }); - }); } else if (isSafari && navigator.serviceWorker) { // Unregister any existing SW in Safari to clean up stale state navigator.serviceWorker.getRegistrations().then(regs => regs.forEach(r => r.unregister())); diff --git a/js/map.js b/js/map.js index 39ef068..eaf88b3 100644 --- a/js/map.js +++ b/js/map.js @@ -2,7 +2,7 @@ // MAP // ═══════════════════════════════════════ function _styleUrl() { - if (_mapStyle === 'satellite') return STYLE_SAT; + if (_mapStyle === 'satellite' || _mapStyle === 'satellite3d') return STYLE_SAT; if (_mapStyle === 'dark') return STYLE_DARK; if (_mapStyle === 'bright') return STYLE_BRIGHT; return STYLE_STREET; // light, enriched, terrain3d, globe all use Liberty as base @@ -236,7 +236,7 @@ function _scaledTextSize(orig) { } function applyLabelScale() { - if (_mapStyle === 'satellite') return; + if (_mapStyle === 'satellite' || _mapStyle === 'satellite3d') return; const style = map.getStyle(); if (!style || !style.layers) return; for (const layer of style.layers) { @@ -251,7 +251,7 @@ function applyLabelScale() { } function applyLabelVisibility() { - if (_mapStyle === 'satellite') return; + if (_mapStyle === 'satellite' || _mapStyle === 'satellite3d') return; const vis = labelsVisible ? 'visible' : 'none'; const style = map.getStyle(); if (!style || !style.layers) return; @@ -276,7 +276,7 @@ function toggleLabels() { // Apply vivid parks, airport runways, mountain peaks, and optional 3D buildings function applyExtraLayers() { const hasOmt = map.getSource && map.getSource('openmaptiles'); - if (!hasOmt || ['satellite', 'globe'].includes(_mapStyle)) return; + if (!hasOmt || ['satellite', 'satellite3d', 'globe'].includes(_mapStyle)) return; // Vivid park fill β€” more saturated green over the base park layer if (!map.getLayer('matrix-park-vivid')) { @@ -357,7 +357,7 @@ function applyExtraLayers() { function apply3DBuildings() { if (!map.getSource || !map.getSource('openmaptiles')) return; - const shouldShow = buildings3DVisible && !['satellite', 'terrain3d', 'globe'].includes(_mapStyle); + const shouldShow = buildings3DVisible && !['satellite', 'satellite3d', 'terrain3d', 'globe'].includes(_mapStyle); if (shouldShow && !map.getLayer('matrix-buildings-3d')) { // Insert below the first symbol layer so road/POI labels render on top of buildings const firstSymbol = map.getStyle()?.layers?.find(l => l.type === 'symbol'); @@ -418,7 +418,7 @@ function addPinLayers() { if (!alreadyThere) { // In 3D Terrain (pitched view), offset the focal point downward in screen space // so the pin appears in the visible ground area rather than drifting off-screen - const pitchOffset = _mapStyle === 'terrain3d' ? [0, Math.round(map.transform?.height * 0.15 || 100)] : [0, 0]; + const pitchOffset = (_mapStyle === 'terrain3d' || _mapStyle === 'satellite3d') ? [0, Math.round(map.transform?.height * 0.15 || 100)] : [0, 0]; map.flyTo({ center: [lng, lat], zoom: targetZoom, speed: 0.8, curve: 1.0, essential: true, offset: pitchOffset, easing: t => t < 0.5 ? 4*t*t*t : 1 - Math.pow(-2*t+2, 3)/2 }); @@ -446,7 +446,7 @@ function applyTheme() { _tileTemplatesCache = null; // Set initial button label const btn = document.getElementById('tb-style-btn'); - const labels = { light: 'Light Map', bright: 'Bright Map', enriched: 'Terrain', dark: 'Dark Map', satellite: 'Satellite', terrain3d: '3D Terrain', globe: 'Globe' }; + const labels = { light: 'Light Map', bright: 'Bright Map', enriched: 'Terrain', dark: 'Dark Map', satellite: 'Satellite', satellite3d: '3D Satellite', terrain3d: '3D Terrain', globe: 'Globe' }; if (btn) btn.textContent = (labels[_mapStyle] || _mapStyle) + ' β–Ύ'; // Set initial active state in menu @@ -624,7 +624,7 @@ async function initMap() { map.getCanvas().addEventListener('mouseout', () => { coordsEl.style.display = 'none'; }); const updateZoom = () => { zoomEl.textContent = 'z' + map.getZoom().toFixed(2); - const is3D = _mapStyle === 'terrain3d'; + const is3D = _mapStyle === 'terrain3d' || _mapStyle === 'satellite3d'; if (is3D) { pitchWrap.style.display = 'flex'; pitchEl.textContent = `p${map.getPitch().toFixed(0)}Β° b${map.getBearing().toFixed(0)}Β°`; @@ -676,7 +676,7 @@ async function initMap() { bldgBtn.innerHTML = '3D'; bldgBtn.addEventListener('click', toggle3DBuildings); bldgWrap.appendChild(bldgBtn); - if (['satellite', 'terrain3d', 'globe'].includes(_mapStyle)) bldgWrap.style.display = 'none'; + if (['satellite', 'satellite3d', 'terrain3d', 'globe'].includes(_mapStyle)) bldgWrap.style.display = 'none'; navGroup.before(bldgWrap); } applyLabelScale(); @@ -701,7 +701,7 @@ async function initMap() { // Water clicks allowed at any zoom; land clicks require zoom >= 5. // Satellite/terrain3d/globe have no vector layers for water detection. const allHits = map.queryRenderedFeatures(e.point); - const noVectorLayers = ['satellite', 'terrain3d', 'globe'].includes(_mapStyle); + const noVectorLayers = ['satellite', 'satellite3d', 'terrain3d', 'globe'].includes(_mapStyle); const isWater = !noVectorLayers && allHits.some(f => f.layer.type === 'fill' && /^(water|ocean)/.test(f.layer.id)); // If style is mid-transition (queryRenderedFeatures returns nothing), treat as land if (!isWater && map.getZoom() < 5) return; @@ -765,7 +765,7 @@ async function initMap() { // Elevation β€” only in 3D Terrain mode let elevationStr = ''; - if (_mapStyle === 'terrain3d') { + if (_mapStyle === 'terrain3d' || _mapStyle === 'satellite3d') { try { const elev = map.queryTerrainElevation([lng, lat]); if (elev !== null && elev !== undefined) { @@ -871,21 +871,21 @@ function toggleStyleMenu(e) { function setMapStyle(mode) { const wasDark = _mapStyle === 'dark'; _mapStyle = mode; - // Persist style preference (satellite resets to previous on reload) - if (mode !== 'satellite') localStorage.setItem('matrix-theme', mode); + // Persist style preference (satellite/satellite3d reset to previous on reload) + if (mode !== 'satellite' && mode !== 'satellite3d') localStorage.setItem('matrix-theme', mode); // Defer dark-map CSS class removal until pin icons are re-added with correct // compensation (otherwise pre-darkened images render without the CSS filter) const mapEl = document.getElementById('map'); if (_mapStyle === 'dark') mapEl.classList.add('dark-map'); - mapEl.classList.toggle('sat-mode', ['satellite', 'terrain3d', 'globe'].includes(_mapStyle)); + mapEl.classList.toggle('sat-mode', ['satellite', 'satellite3d', 'terrain3d', 'globe'].includes(_mapStyle)); // Labels toggle visibility const labelsWrap = document.getElementById('labels-toggle-wrap'); - if (labelsWrap) labelsWrap.style.visibility = _mapStyle === 'satellite' ? 'hidden' : 'visible'; + if (labelsWrap) labelsWrap.style.visibility = (_mapStyle === 'satellite' || _mapStyle === 'satellite3d') ? 'hidden' : 'visible'; // Hide 3D buildings toggle in modes where buildings don't make sense const bldgWrap = document.getElementById('buildings-toggle-wrap'); - if (bldgWrap) bldgWrap.style.display = ['satellite', 'terrain3d', 'globe'].includes(_mapStyle) ? 'none' : ''; + if (bldgWrap) bldgWrap.style.display = ['satellite', 'satellite3d', 'terrain3d', 'globe'].includes(_mapStyle) ? 'none' : ''; // Disable Export Video in Globe mode (flyTo animation doesn't translate to globe projection) const exportBtn = document.getElementById('tb-export-video'); @@ -895,7 +895,7 @@ function setMapStyle(mode) { } // Update button label - const labels = { light: 'Light Map', bright: 'Bright Map', enriched: 'Terrain', dark: 'Dark Map', satellite: 'Satellite', terrain3d: '3D Terrain', globe: 'Globe' }; + const labels = { light: 'Light Map', bright: 'Bright Map', enriched: 'Terrain', dark: 'Dark Map', satellite: 'Satellite', satellite3d: '3D Satellite', terrain3d: '3D Terrain', globe: 'Globe' }; const btn = document.getElementById('tb-style-btn'); if (btn) btn.textContent = (labels[mode] || mode) + ' β–Ύ'; @@ -914,7 +914,7 @@ function setMapStyle(mode) { } function _applyTerrainAndProjection() { - const is3D = _mapStyle === 'terrain3d'; + const is3D = _mapStyle === 'terrain3d' || _mapStyle === 'satellite3d'; const isGlobe = _mapStyle === 'globe'; // Set projection FIRST β€” before any camera moves β€” so easeTo never runs under the wrong projection @@ -956,7 +956,7 @@ function _applyTerrainAndProjection() { if (map.getLayer('sky') === undefined) { map.addLayer({ id: 'sky', type: 'sky', paint: { 'sky-type': 'gradient', 'sky-gradient': ['interpolate', ['linear'], ['sky-radial-progress'], 0, 'rgba(255,255,255,0)', 0.5, '#87cefa', 1, '#4682b4'] } }); } - if (!map.getLayer('terrain-hillshade')) { + if (_mapStyle !== 'satellite3d' && !map.getLayer('terrain-hillshade')) { // Insert below the first road/label layer so hillshading shows through const firstSymbol = map.getStyle().layers.find(l => l.type === 'line' || l.type === 'symbol'); map.addLayer({ diff --git a/js/search.js b/js/search.js index 2cbfd0f..8796602 100644 --- a/js/search.js +++ b/js/search.js @@ -9,12 +9,41 @@ const dClear = document.getElementById('dest-clear'); const _searchCache = {}; let _searchMoveTimer = null; +let _searchCategory = 'all'; + +const POI_CATEGORIES = { + hotel: { label: '🏨 Hotel', tags: [['amenity','hotel'],['tourism','hotel']] }, + restaurant: { label: '🍽 Restaurant', tags: [['amenity','restaurant'],['amenity','cafe'],['amenity','bar'],['amenity','pub'],['amenity','fast_food']] }, + attraction: { label: 'πŸ› Attraction', tags: [['tourism','attraction'],['tourism','museum'],['tourism','viewpoint']] }, +}; + +function toggleSearchCatMenu() { + document.getElementById('search-cat-menu').classList.toggle('open'); +} + +function setSearchCat(cat) { + _searchCategory = cat; + document.getElementById('search-cat-label').textContent = cat === 'all' ? 'All' : POI_CATEGORIES[cat].label; + document.getElementById('search-cat-btn').classList.toggle('active', cat !== 'all'); + document.querySelectorAll('.search-cat-item').forEach(el => el.classList.toggle('active', el.dataset.cat === cat)); + document.getElementById('search-cat-menu').classList.remove('open'); + Object.keys(_searchCache).forEach(k => delete _searchCache[k]); + const q = dInput.value.trim(); + if (q.length >= 2) { + dResults.style.display = 'block'; + runDestSearch(q); + } else if (cat !== 'all') { + dResults.style.display = 'block'; + dLoading.style.display = 'none'; + dResults.innerHTML = '
Type to search nearby
'; + } +} // Re-run visible search when the map viewport changes (pan/zoom) function _onMapMoveForSearch() { clearTimeout(_searchMoveTimer); const q = dInput.value.trim(); - if (q.length < 2 || dResults.style.display === 'none' || destMarkerObj) return; + if ((q.length < 2 && _searchCategory === 'all') || dResults.style.display === 'none' || destMarkerObj) return; _searchMoveTimer = setTimeout(() => runDestSearch(q), 600); } @@ -22,12 +51,13 @@ dInput.addEventListener('input', () => { const q = dInput.value.trim(); dClear.style.display = q ? 'block' : 'none'; clearTimeout(searchTimer); - if (q.length < 2) { dResults.style.display='none'; return; } + if (q.length < 2 && _searchCategory === 'all') { dResults.style.display='none'; return; } searchTimer = setTimeout(() => runDestSearch(q), 380); }); dInput.addEventListener('keydown', e => { if(e.key==='Escape'){clearDestSearch();dInput.blur();} }); document.addEventListener('click', e => { if (!document.getElementById('dest-search-wrap').contains(e.target)) dResults.style.display='none'; + if (!document.getElementById('search-cat-wrap').contains(e.target)) document.getElementById('search-cat-menu').classList.remove('open'); }); @@ -130,6 +160,69 @@ async function runReverseGeoSearch(lat, lng) { } } +async function runCategorySearch(q, cat, cacheKey) { + if (!map || q.length < 2) { + dLoading.style.display = 'none'; + dResults.innerHTML = '
Type to search nearby
'; + return; + } + const b = map.getBounds(); + + // --- Attempt 1: Overpass via local proxy (precise tag-based POI search) --- + const bbox = `(${b.getSouth().toFixed(5)},${b.getWest().toFixed(5)},${b.getNorth().toFixed(5)},${b.getEast().toFixed(5)})`; + const safe = q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const nameFilter = `["name"~"${safe}",i]`; + const stmts = POI_CATEGORIES[cat].tags.flatMap(([k, v]) => [ + `node["${k}"="${v}"]${nameFilter}${bbox};`, + `way["${k}"="${v}"]${nameFilter}${bbox};`, + `relation["${k}"="${v}"]${nameFilter}${bbox};`, + ]).join('\n '); + const query = `[out:json][timeout:10];\n(\n ${stmts}\n);\nout center 20;`; + try { + const r = await fetch('/api/overpass', { method: 'POST', body: new URLSearchParams({ data: query }) }); + if (r.ok) { + const data = await r.json(); + const results = (data.elements || []) + .filter(el => el.tags?.name) + .map(el => { + const t = el.tags, lat = el.lat ?? el.center?.lat, lon = el.lon ?? el.center?.lon; + if (lat == null || lon == null) return null; + const city = t['addr:city'] || t['addr:town'] || t['addr:village'] || ''; + const country = t['addr:country'] || ''; + return { + display_name: [t.name, city, country].filter(Boolean).join(', '), + lat: String(lat), lon: String(lon), + address: { city, country } + }; + }) + .filter(Boolean); + _searchCache[cacheKey] = results; + renderSearchResults(results); + return; + } + } catch(_) { /* proxy unreachable β€” fall through to Nominatim */ } + + // --- Fallback: Nominatim bounded=1 + layer=poi (works everywhere, less precise) --- + const viewbox = `&viewbox=${b.getWest()},${b.getNorth()},${b.getEast()},${b.getSouth()}&bounded=1&layer=poi`; + const searchWait = Math.max(0, 1100 - (Date.now() - _lastNominatimCall)); + if (searchWait > 0) await new Promise(r => setTimeout(r, searchWait)); + _lastNominatimCall = Date.now(); + try { + const r = await fetch( + `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(q)}&format=json&limit=20&addressdetails=1${viewbox}`, + { headers: { 'Accept-Language': 'en' } } + ); + if (!r.ok) throw new Error(`HTTP ${r.status}`); + const data = await r.json(); + _searchCache[cacheKey] = data; + renderSearchResults(data); + } catch(err) { + console.warn('Category search failed:', err); + dLoading.style.display = 'none'; + dResults.innerHTML = `
Search failed β€” try again
`; + } +} + async function runDestSearch(q) { if (_isOffline) { dResults.style.display='block'; dLoading.style.display='none'; @@ -153,8 +246,13 @@ async function runDestSearch(q) { } const zoom = map ? map.getZoom() : 0; - const cacheKey = q + (zoom >= 3 ? `@${map.getCenter().lng.toFixed(1)},${map.getCenter().lat.toFixed(1)}` : ''); + const cacheKey = q + (zoom >= 3 ? `@${map.getCenter().lng.toFixed(1)},${map.getCenter().lat.toFixed(1)}` : '') + (_searchCategory !== 'all' ? `#${_searchCategory}` : ''); if (_searchCache[cacheKey]) { renderSearchResults(_searchCache[cacheKey]); return; } + + if (_searchCategory !== 'all') { + await runCategorySearch(q, _searchCategory, cacheKey); + return; + } try { const searchWait = Math.max(0, 1100 - (Date.now() - _lastNominatimCall)); if (searchWait > 0) await new Promise(r => setTimeout(r, searchWait)); diff --git a/serve.py b/serve.py index 5a60c01..9329c2f 100644 --- a/serve.py +++ b/serve.py @@ -182,7 +182,7 @@ def setup_vendor(): PHOTO_RE = re.compile(r"^/api/photos/([a-zA-Z0-9_-]+)(/thumb)?$") # Allowed tile origins for the proxy -TILE_ALLOWED_HOSTS = {'tiles.openfreemap.org', 'server.arcgisonline.com'} +TILE_ALLOWED_HOSTS = {'tiles.openfreemap.org', 'tiles.maps.eox.at'} # Video export streaming state: session_id -> {path, file, mime} _video_sessions = {} @@ -306,6 +306,8 @@ def do_POST(self): try: if self.path == "/api/data": self._save_data() + elif self.path == "/api/overpass": + self._overpass_query() elif self.path.startswith("/api/tiles/cache?"): self._cache_tile() elif self.path == "/api/video/start": @@ -453,6 +455,28 @@ def _send_tile(self, data, content_type): except (ConnectionResetError, BrokenPipeError): pass # Client disconnected (e.g. user panned away, SW timeout) + def _overpass_query(self): + """Proxy Overpass API queries through serve.py to bypass network-level browser restrictions.""" + length = int(self.headers.get('Content-Length', 0)) + body = self.rfile.read(length) if length else b'' + try: + req = urllib.request.Request( + 'https://overpass-api.de/api/interpreter', + data=body, + headers={'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': 'Matrix-travel-app/1.0'} + ) + with _urlopen(req) as resp: + data = resp.read() + self.send_response(200) + self.send_header('Content-Type', 'application/json') + self.end_headers() + self.wfile.write(data) + except Exception as e: + self.send_response(502) + self.end_headers() + self.wfile.write(str(e).encode()) + def _proxy_tile(self): """Serve tile from disk cache only. Returns 404 if not cached.""" from urllib.parse import urlparse, parse_qs diff --git a/sw.js b/sw.js index 1c2d39d..193cda0 100644 --- a/sw.js +++ b/sw.js @@ -1,5 +1,5 @@ // Matrix β€” Service Worker for offline support -const CACHE_VERSION = 'matrix-v19'; +const CACHE_VERSION = 'matrix-v20'; const APP_CACHE = `${CACHE_VERSION}-app`; const TILE_CACHE = `${CACHE_VERSION}-tiles`; @@ -29,7 +29,7 @@ const APP_SHELL = [ // Tile URL patterns to cache (raster + vector tiles, sprites, glyphs) const TILE_PATTERNS = [ /tiles\.openfreemap\.org\/planet\//, // vector tiles only (not style JSON) - /server\.arcgisonline\.com/, + /tiles\.maps\.eox\.at/, /\.pbf(\?|$)/, // vector tile protobuf files /sprites?\//, // map sprites /glyphs?\//, // map font glyphs @@ -38,9 +38,6 @@ const TILE_PATTERNS = [ // Max cached tiles (LRU eviction when exceeded) const MAX_TILES = 10000; -// Local server port for disk-cached tile proxy -let serverPort = 8765; - console.log(`SW: ${CACHE_VERSION} loaded`); self.addEventListener('install', (event) => { @@ -55,10 +52,6 @@ self.addEventListener('install', (event) => { self.skipWaiting(); }); -self.addEventListener('message', (event) => { - if (event.data?.type === 'set-port') serverPort = event.data.port; -}); - self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((keys) => { @@ -159,7 +152,7 @@ async function tileStrategy(request) { const cached = await caches.match(request, { ignoreVary: true }); if (cached) return cached; - const proxyUrl = `http://localhost:${serverPort}/api/tiles/proxy?url=${encodeURIComponent(request.url)}`; + const proxyUrl = `${self.location.origin}/api/tiles/proxy?url=${encodeURIComponent(request.url)}`; const cacheAndReturn = async (body, ct) => { try { @@ -184,7 +177,7 @@ async function tileStrategy(request) { return r.arrayBuffer().then(body => { const ct = r.headers.get('Content-Type') || 'application/octet-stream'; // Save to disk in background - fetch(`http://localhost:${serverPort}/api/tiles/cache?url=${encodeURIComponent(request.url)}`, { + fetch(`${self.location.origin}/api/tiles/cache?url=${encodeURIComponent(request.url)}`, { method: 'POST', body: body.slice(0) }).catch(() => {}); return { body, ct };