Add progressive web app companion for cross-platform access

Vite + TypeScript PWA that mirrors the Android app's core features:
- Pre-processed shelter data (build-time UTM33N→WGS84 conversion)
- Leaflet map with shelter markers, user location, and offline tiles
- Canvas compass arrow (ported from DirectionArrowView.kt)
- IndexedDB shelter cache with 7-day staleness check
- Service worker with CacheFirst tiles and precached app shell
- i18n for en, nb, nn (ported from Android strings.xml)
- iOS/Android compass handling with low-pass filter
- Respects user map interaction (no auto-snap on pan/zoom)
- Build revision cache-breaker for reliable SW updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-03-08 17:41:38 +01:00
commit e8428de775
12051 changed files with 1799735 additions and 0 deletions

View file

@ -0,0 +1,84 @@
import {
cyan,
dim,
green,
magenta,
yellow
} from "./chunk-UB6OAFZF.js";
// src/utils.ts
function slash(str) {
return str.replace(/\\/g, "/");
}
function resolveBasePath(base) {
if (isAbsolute(base))
return base;
return !base.startsWith("/") && !base.startsWith("./") ? `/${base}` : base;
}
function isAbsolute(url) {
return url.match(/^(?:[a-z]+:)?\/\//i);
}
function normalizePath(path) {
return path.replace(/\\/g, "/");
}
// src/log.ts
import { relative } from "node:path";
function logSWViteBuild(version, swName, viteOptions, format) {
const { logLevel = "info" } = viteOptions;
if (logLevel === "silent")
return;
if (logLevel === "info") {
console.info([
"",
`${cyan(`PWA v${version}`)}`,
`Building ${magenta(swName)} service worker ("${magenta(format)}" format)...`
].join("\n"));
}
}
function logWorkboxResult(version, throwMaximumFileSizeToCacheInBytes, strategy, buildResult, viteOptions, format = "none") {
if (throwMaximumFileSizeToCacheInBytes) {
const entries = buildResult.warnings.filter((w) => w.includes("maximumFileSizeToCacheInBytes"));
if (entries.length) {
const prefix = strategy === "generateSW" ? "workbox" : "injectManifest";
throw new Error(`
Configure "${prefix}.maximumFileSizeToCacheInBytes" to change the limit: the default value is 2 MiB.
Check https://vite-pwa-org.netlify.app/guide/faq.html#missing-assets-from-sw-precache-manifest for more information.
Assets exceeding the limit:
${entries.map((w) => ` - ${w.replace(". Configure maximumFileSizeToCacheInBytes to change this limit", "")}`).join("\n")}
`);
}
}
const { root, logLevel = "info" } = viteOptions;
if (logLevel === "silent")
return;
const { count, size, filePaths, warnings } = buildResult;
if (logLevel === "info") {
const entries = [
"",
`${cyan(`PWA v${version}`)}`,
`mode ${magenta(strategy)}`
];
if (strategy === "injectManifest")
entries.push(`format: ${magenta(format)}`);
entries.push(
`precache ${green(`${count} entries`)} ${dim(`(${(size / 1024).toFixed(2)} KiB)`)}`,
"files generated",
...filePaths.map((p) => ` ${dim(normalizePath(relative(root, p)))}`)
);
console.info(entries.join("\n"));
}
warnings && warnings.length > 0 && console.warn(yellow([
"warnings",
...warnings.map((w) => ` ${w}`),
""
].join("\n")));
}
export {
slash,
resolveBasePath,
normalizePath,
logSWViteBuild,
logWorkboxResult
};