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,46 @@
import { stripAnsi } from "../strip-ansi/index.js";
import { eastAsianWidth } from "../eastasianwidth/index.js";
import { emojiRegex } from "../emoji-regex/index.js";
export function stringWidth(str, options = {}) {
if (typeof str !== "string" || str.length === 0) {
return 0;
}
options = {
ambiguousIsNarrow: true,
...options,
};
str = stripAnsi(str);
if (str.length === 0) {
return 0;
}
str = str.replace(emojiRegex(), " ");
const ambiguousCharacterWidth = options.ambiguousIsNarrow ? 1 : 2;
let width = 0;
for (const character of str) {
const codePoint = character.codePointAt(0);
// Ignore control characters
if (!codePoint ||
codePoint <= 0x1f ||
(codePoint >= 0x7f && codePoint <= 0x9f)) {
continue;
}
// Ignore combining characters
if (codePoint >= 0x300 && codePoint <= 0x36f) {
continue;
}
const code = eastAsianWidth(character);
switch (code) {
case "F":
case "W":
width += 2;
break;
case "A":
width += ambiguousCharacterWidth;
break;
default:
width += 1;
}
}
return width;
}
//# sourceMappingURL=index.js.map