wannpassts/frontend/vite.config.ts
Tronax cbc03c56bf
feat: add PWA support and mobile-responsive layouts
- Add web app manifest, maskable/apple-touch icons, and a production-only
  service worker that caches the app shell for offline start
- Improve mobile UX: responsive breakpoints from ~320px, 44px touch
  targets, safe-area insets, dvh heights, 16px inputs to avoid iOS zoom
- Serve .webmanifest with correct content type in the Go static handler
- Document PWA behavior and mobile features in the README
2026-08-25 19:12:12 +02:00

99 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { defineConfig, type Plugin } from 'vite'
import vue from '@vitejs/plugin-vue'
// Service Worker als Build-Artefakt: Die Cache-Version ist der Build-Zeitstempel,
// damit Browser nach jedem Deploy den SW aktualisieren und alte Caches räumen.
const SW_SOURCE = `
const VERSION = 'wp-__SW_VERSION__';
const CACHE_NAME = 'wannpassts-' + VERSION;
const PRECACHE = [
'/',
'/manifest.webmanifest',
'/favicon.svg',
'/icons/icon-192.png',
'/icons/icon-512.png',
'/icons/icon-maskable-512.png',
'/icons/apple-touch-icon.png',
];
self.addEventListener('install', (event) => {
event.waitUntil((async () => {
const cache = await caches.open(CACHE_NAME);
await cache.addAll(PRECACHE).catch(() => {});
await self.skipWaiting();
})());
});
self.addEventListener('activate', (event) => {
event.waitUntil((async () => {
const keys = await caches.keys();
await Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)));
await self.clients.claim();
})());
});
self.addEventListener('fetch', (event) => {
const req = event.request;
if (req.method !== 'GET') return;
const url = new URL(req.url);
if (url.origin !== self.location.origin) return;
// API-Aufrufe immer live niemals aus dem Cache bedienen.
if (url.pathname.startsWith('/api/')) return;
// Navigation: Netzwerk zuerst, offline App-Shell aus dem Cache.
if (req.mode === 'navigate') {
event.respondWith((async () => {
try {
const res = await fetch(req);
const cache = await caches.open(CACHE_NAME);
cache.put('/', res.clone());
return res;
} catch (err) {
const cached = (await caches.match(req)) || (await caches.match('/'));
if (cached) return cached;
return new Response('Offline bitte Internetverbindung prüfen.', {
status: 503,
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
}
})());
return;
}
// Statische Assets: Cache zuerst (gehashte Namen sind unveränderlich).
event.respondWith((async () => {
const cached = await caches.match(req, { ignoreSearch: true });
if (cached) return cached;
const res = await fetch(req);
if (res.ok) {
const cache = await caches.open(CACHE_NAME);
cache.put(req, res.clone());
}
return res;
})());
});
`
function serviceWorkerPlugin(): Plugin {
return {
name: 'wannpassts-sw',
apply: 'build',
generateBundle() {
this.emitFile({
type: 'asset',
fileName: 'sw.js',
source: SW_SOURCE.replace('__SW_VERSION__', new Date().toISOString()),
})
},
}
}
export default defineConfig({
plugins: [vue(), serviceWorkerPlugin()],
server: {
port: 5173,
proxy: {
'/api': { target: 'http://localhost:8080', changeOrigin: true },
},
},
})