> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/koala73/worldmonitor/llms.txt
> Use this file to discover all available pages before exploring further.

# Progressive Web App (PWA)

> PWA configuration, service worker, offline support, and installability

World Monitor is a fully-featured Progressive Web App (PWA) that works offline, installs to the home screen, and provides a native app-like experience.

## PWA Features

* **Offline Support** - Core functionality available without internet
* **Installable** - Add to home screen on mobile and desktop
* **Fast Loading** - Service worker caches assets and data
* **Background Sync** - Queue updates when offline
* **Push Notifications** - Real-time alerts (optional)

## Configuration

### Vite PWA Plugin

World Monitor uses `vite-plugin-pwa` with Workbox:

```typescript vite.config.ts theme={null}
import { VitePWA } from 'vite-plugin-pwa';

export default defineConfig({
  plugins: [
    VitePWA({
      registerType: 'autoUpdate',
      injectRegister: false,
      
      includeAssets: [
        'favico/favicon.ico',
        'favico/apple-touch-icon.png',
        'favico/favicon-32x32.png',
      ],
      
      manifest: {
        name: 'World Monitor - Real-Time Global Intelligence Dashboard',
        short_name: 'WorldMonitor',
        description: 'Real-time global intelligence dashboard with live news, markets, military tracking, and geopolitical data.',
        start_url: '/',
        scope: '/',
        display: 'standalone',
        orientation: 'any',
        theme_color: '#0a0f0a',
        background_color: '#0a0f0a',
        categories: ['news', 'productivity'],
        icons: [
          { src: '/favico/android-chrome-192x192.png', sizes: '192x192', type: 'image/png' },
          { src: '/favico/android-chrome-512x512.png', sizes: '512x512', type: 'image/png' },
          { src: '/favico/android-chrome-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
        ],
      },
      
      workbox: {
        globPatterns: ['**/*.{js,css,ico,png,svg,woff2}'],
        globIgnores: ['**/ml*.js', '**/onnx*.wasm', '**/locale-*.js'],
        navigateFallback: null,
        skipWaiting: true,
        clientsClaim: true,
        cleanupOutdatedCaches: true,
      },
    }),
  ],
});
```

## Web App Manifest

### Manifest Fields

The web app manifest defines how World Monitor appears when installed:

```json manifest.webmanifest theme={null}
{
  "name": "World Monitor - Real-Time Global Intelligence Dashboard",
  "short_name": "WorldMonitor",
  "description": "Real-time global intelligence dashboard with live news, markets, military tracking, and geopolitical data.",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "orientation": "any",
  "theme_color": "#0a0f0a",
  "background_color": "#0a0f0a",
  "categories": ["news", "productivity"],
  "icons": [
    {
      "src": "/favico/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/favico/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    },
    {
      "src": "/favico/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ]
}
```

### Display Modes

* **standalone** - Full-screen without browser UI (recommended)
* **fullscreen** - Full-screen with no browser or OS UI
* **minimal-ui** - Minimal browser UI (back/forward buttons)
* **browser** - Normal browser tab

World Monitor uses `standalone` for an app-like experience.

### Maskable Icons

Maskable icons adapt to different device shapes (rounded corners, circles):

```json theme={null}
{
  "src": "/favico/android-chrome-512x512.png",
  "sizes": "512x512",
  "type": "image/png",
  "purpose": "maskable"
}
```

Design maskable icons with a safe zone (80% of canvas) for important content.

### Variant-Specific Manifests

Each variant has custom metadata:

```typescript vite.config.ts theme={null}
const VARIANT_META = {
  full: {
    siteName: 'World Monitor',
    shortName: 'WorldMonitor',
    categories: ['news', 'productivity'],
  },
  tech: {
    siteName: 'Tech Monitor',
    shortName: 'TechMonitor',
    categories: ['news', 'business'],
  },
  finance: {
    siteName: 'Finance Monitor',
    shortName: 'FinanceMonitor',
    categories: ['finance', 'news'],
  },
  happy: {
    siteName: 'Happy Monitor',
    shortName: 'HappyMonitor',
    categories: ['news', 'lifestyle'],
  },
};

const activeVariant = process.env.VITE_VARIANT || 'full';
const activeMeta = VARIANT_META[activeVariant];

VitePWA({
  manifest: {
    name: `${activeMeta.siteName} - ${activeMeta.subject}`,
    short_name: activeMeta.shortName,
    categories: activeMeta.categories,
  },
});
```

## Service Worker

### Workbox Configuration

World Monitor uses Workbox for advanced caching strategies:

```typescript vite.config.ts theme={null}
workbox: {
  // Files to precache
  globPatterns: ['**/*.{js,css,ico,png,svg,woff2}'],
  
  // Exclude heavy files from precache
  globIgnores: ['**/ml*.js', '**/onnx*.wasm', '**/locale-*.js'],
  
  // Don't use navigation fallback (breaks API routes)
  navigateFallback: null,
  
  // Update immediately instead of waiting
  skipWaiting: true,
  
  // Take control of all clients immediately
  clientsClaim: true,
  
  // Remove old caches on activation
  cleanupOutdatedCaches: true,
}
```

### Caching Strategies

World Monitor uses different strategies for different resource types:

#### HTML Navigation - Network First

```typescript vite.config.ts theme={null}
runtimeCaching: [
  {
    urlPattern: ({ request }) => request.mode === 'navigate',
    handler: 'NetworkFirst',
    options: {
      cacheName: 'html-navigation',
      networkTimeoutSeconds: 3,
    },
  },
]
```

Tries network first, falls back to cache if network fails or times out after 3 seconds.

#### API Routes - Network Only

```typescript vite.config.ts theme={null}
{
  urlPattern: ({ url, sameOrigin }) => sameOrigin && /^\/api\//.test(url.pathname),
  handler: 'NetworkOnly',
  method: 'GET',
}
```

Never caches API responses to ensure fresh data.

#### Map Tiles - Cache First

```typescript vite.config.ts theme={null}
{
  urlPattern: /^https:\/\/api\.maptiler\.com\//,
  handler: 'CacheFirst',
  options: {
    cacheName: 'map-tiles',
    expiration: {
      maxEntries: 500,
      maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
    },
    cacheableResponse: {
      statuses: [0, 200],
    },
  },
}
```

Caches map tiles aggressively for offline use.

#### Fonts - Cache First

```typescript vite.config.ts theme={null}
{
  urlPattern: /^https:\/\/fonts\.gstatic\.com\//,
  handler: 'CacheFirst',
  options: {
    cacheName: 'google-fonts-woff',
    expiration: {
      maxEntries: 30,
      maxAgeSeconds: 365 * 24 * 60 * 60, // 1 year
    },
    cacheableResponse: {
      statuses: [0, 200],
    },
  },
}
```

#### Images - Stale While Revalidate

```typescript vite.config.ts theme={null}
{
  urlPattern: /\.(?:png|jpg|jpeg|svg|gif|webp)$/i,
  handler: 'StaleWhileRevalidate',
  options: {
    cacheName: 'images',
    expiration: {
      maxEntries: 100,
      maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
    },
  },
}
```

Serves from cache immediately, updates in background.

### Service Worker Registration

World Monitor registers the service worker automatically:

```typescript theme={null}
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js');
  });
}
```

### Service Worker Updates

With `skipWaiting: true` and `autoUpdate`, new versions activate immediately:

```typescript theme={null}
VitePWA({
  registerType: 'autoUpdate',
  workbox: {
    skipWaiting: true,
    clientsClaim: true,
  },
});
```

Users don't need to close all tabs to get updates.

## Offline Support

### Offline Page

World Monitor serves a custom offline page when no cache is available:

```html public/offline.html theme={null}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Offline - World Monitor</title>
</head>
<body>
  <h1>You're offline</h1>
  <p>World Monitor requires an internet connection for real-time data.</p>
  <p>Cached data will load when you reconnect.</p>
</body>
</html>
```

### Offline Detection

Detect network status in your app:

```typescript theme={null}
const [isOnline, setIsOnline] = useState(navigator.onLine);

useEffect(() => {
  const handleOnline = () => setIsOnline(true);
  const handleOffline = () => setIsOnline(false);
  
  window.addEventListener('online', handleOnline);
  window.addEventListener('offline', handleOffline);
  
  return () => {
    window.removeEventListener('online', handleOnline);
    window.removeEventListener('offline', handleOffline);
  };
}, []);
```

### Background Sync

Queue actions when offline, sync when online:

```typescript theme={null}
if ('serviceWorker' in navigator && 'SyncManager' in window) {
  navigator.serviceWorker.ready.then((registration) => {
    registration.sync.register('sync-data');
  });
}
```

## Installation

### Install Prompt

World Monitor can prompt users to install:

```typescript theme={null}
let deferredPrompt: any;

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  deferredPrompt = e;
  // Show custom install button
});

function handleInstall() {
  if (deferredPrompt) {
    deferredPrompt.prompt();
    deferredPrompt.userChoice.then((choiceResult: any) => {
      if (choiceResult.outcome === 'accepted') {
        console.log('User accepted install');
      }
      deferredPrompt = null;
    });
  }
}
```

### Installation Criteria

Browsers show the install prompt when:

1. The app is served over HTTPS
2. A valid web app manifest exists
3. A service worker is registered
4. The user has visited at least once
5. The user has engaged with the site

### Installation Methods

<Tabs>
  <Tab title="Chrome Desktop">
    1. Click the install icon in the address bar
    2. Or: Menu → Install World Monitor
  </Tab>

  <Tab title="Safari iOS">
    1. Tap the Share button
    2. Scroll down and tap "Add to Home Screen"
  </Tab>

  <Tab title="Chrome Android">
    1. Tap the menu (⋮)
    2. Tap "Install app" or "Add to Home screen"
  </Tab>

  <Tab title="Edge Desktop">
    1. Click the app icon in the address bar
    2. Or: Menu → Apps → Install World Monitor
  </Tab>
</Tabs>

## Testing

### Lighthouse PWA Audit

Run Lighthouse to check PWA compliance:

```bash theme={null}
npm install -g lighthouse
lighthouse https://worldmonitor.app --view
```

Target scores:

* **Performance**: 90+
* **Accessibility**: 95+
* **Best Practices**: 95+
* **SEO**: 95+
* **PWA**: 100

### Service Worker Testing

Test service worker in Chrome DevTools:

1. Open DevTools (F12)
2. Go to Application → Service Workers
3. Check "Offline" to simulate offline mode
4. Verify caching in Application → Cache Storage

### Manifest Testing

Validate manifest in DevTools:

1. Go to Application → Manifest
2. Check all fields display correctly
3. Verify icons load
4. Test install prompt

### PWA Builder

Use PWA Builder to validate and generate app packages:

```bash theme={null}
# Visit
https://www.pwabuilder.com

# Enter URL
https://worldmonitor.app

# Download packages for Windows Store, Play Store, etc.
```

## Performance Optimization

### Precaching Strategy

Precache only essential assets:

```typescript theme={null}
globPatterns: ['**/*.{js,css,ico,png,svg,woff2}'],
globIgnores: [
  '**/ml*.js',        // ML models (lazy loaded)
  '**/onnx*.wasm',    // ONNX runtime (lazy loaded)
  '**/locale-*.js',   // Locale files (lazy loaded)
],
```

### Cache Size Limits

Set reasonable cache limits:

```typescript theme={null}
expiration: {
  maxEntries: 500,        // Max 500 map tiles
  maxAgeSeconds: 2592000, // 30 days
}
```

### Network Timeouts

Set aggressive timeouts for faster offline fallback:

```typescript theme={null}
handler: 'NetworkFirst',
options: {
  networkTimeoutSeconds: 3, // Fall back to cache after 3s
}
```

## Troubleshooting

### Service Worker Not Updating

**Solution**: Clear cache and unregister old service worker:

```javascript theme={null}
navigator.serviceWorker.getRegistrations().then((registrations) => {
  registrations.forEach((registration) => registration.unregister());
});

caches.keys().then((names) => {
  names.forEach((name) => caches.delete(name));
});
```

### Install Prompt Not Showing

**Checklist**:

1. Served over HTTPS? (localhost works too)
2. Valid manifest.webmanifest?
3. Service worker registered?
4. User visited before?
5. App not already installed?

### Cache Not Working

**Debug**:

1. Open DevTools → Application → Cache Storage
2. Verify caches exist
3. Check service worker status
4. Look for errors in Console

### Manifest Not Loading

**Check**:

1. Manifest path correct in HTML: `<link rel="manifest" href="/manifest.webmanifest">`
2. Manifest served with correct MIME type: `Content-Type: application/manifest+json`
3. No CORS errors (same origin)

## Browser Support

* **Chrome/Edge**: Full PWA support
* **Safari**: PWA support on iOS 11.3+, limited on macOS
* **Firefox**: Service workers and manifest, no install prompt
* **Samsung Internet**: Full PWA support
