handmux 0.12.1 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/public/index.html CHANGED
@@ -5,17 +5,21 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
6
6
  <title>handmux</title>
7
7
 
8
- <!-- Installable PWA: "Add to Home Screen" + standalone (no browser address bar). -->
9
- <link rel="manifest" href="/manifest.webmanifest" />
8
+ <!-- Installable PWA: "Add to Home Screen" + standalone (no browser address bar).
9
+ ?v=2 one-time cache-bust: pre-0.12.2 servers sent these stable-url assets as
10
+ `immutable, max-age=1y`, so returning browsers pinned the OLD icon/manifest for a year.
11
+ index.html is no-store (always fresh), so a new query on the refs escapes those poisoned
12
+ caches at once; staticCache.js now serves them `no-cache`, so no future bump is needed. -->
13
+ <link rel="manifest" href="/manifest.webmanifest?v=2" />
10
14
  <meta name="theme-color" content="#1a1b1e" />
11
- <link rel="icon" type="image/png" href="/icons/icon-192.png" />
15
+ <link rel="icon" type="image/png" href="/icons/icon-192.png?v=2" />
12
16
  <!-- iOS Safari ignores the manifest's display mode, so these drive standalone + the
13
17
  home-screen icon/label. "black" keeps the white status-bar text readable on the dark topbar. -->
14
18
  <meta name="apple-mobile-web-app-capable" content="yes" />
15
19
  <meta name="mobile-web-app-capable" content="yes" />
16
20
  <meta name="apple-mobile-web-app-status-bar-style" content="black" />
17
21
  <meta name="apple-mobile-web-app-title" content="handmux" />
18
- <link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
22
+ <link rel="apple-touch-icon" href="/icons/apple-touch-icon.png?v=2" />
19
23
 
20
24
  <!-- Inline boot splash: paints instantly (before the JS bundle loads) so a cold launch shows the
21
25
  brand, not a blank dark screen. main.jsx calls window.__hideBootSplash() once React has painted;
@@ -44,7 +48,7 @@
44
48
  @keyframes bootDot { 0%,100% { opacity: .22; transform: translateY(0); } 40% { opacity: 1; transform: translateY(-3px); } }
45
49
  @media (prefers-reduced-motion: reduce) { .boot-glow, .boot-dots i { animation: none; opacity: .8; } }
46
50
  </style>
47
- <script type="module" crossorigin src="/assets/index-C5v5QBzw.js"></script>
51
+ <script type="module" crossorigin src="/assets/index-BXd3a0v9.js"></script>
48
52
  <link rel="stylesheet" crossorigin href="/assets/index-DJNPeU_h.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" crossorigin href="/assets/index-DJNPeU_h.css"></noscript>
49
53
  </head>
50
54
  <body>
@@ -9,10 +9,10 @@
9
9
  "background_color": "#1a1b1e",
10
10
  "theme_color": "#1a1b1e",
11
11
  "icons": [
12
- { "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
13
- { "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
14
- { "src": "icons/icon-192-maskable.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" },
15
- { "src": "icons/icon-512-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
12
+ { "src": "icons/icon-192.png?v=2", "sizes": "192x192", "type": "image/png", "purpose": "any" },
13
+ { "src": "icons/icon-512.png?v=2", "sizes": "512x512", "type": "image/png", "purpose": "any" },
14
+ { "src": "icons/icon-192-maskable.png?v=2", "sizes": "192x192", "type": "image/png", "purpose": "maskable" },
15
+ { "src": "icons/icon-512-maskable.png?v=2", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
16
16
  ],
17
17
  "share_target": {
18
18
  "action": "/share-target",
package/public/sw.js CHANGED
@@ -81,11 +81,11 @@ self.addEventListener('push', (event) => {
81
81
  // icon = the large right-side logo. We MUST set it: HyperOS/MIUI always reserves that slot, and an
82
82
  // empty `icon` makes it back-fill the slot with the PWA's own icon at a stray size (the `>` reads as
83
83
  // a lone "v") plus leaves a gap. An explicit app icon fills the slot cleanly.
84
- icon: '/icons/icon-192.png',
84
+ icon: '/icons/icon-192.png?v=2',
85
85
  // badge = the small monochrome status-bar icon. Android reads only its ALPHA and tints the
86
86
  // silhouette; an opaque image (icon-192 has no alpha) can't be used, so it falls back to the
87
87
  // browser's own Chrome logo. badge-96.png is a white `>▮` silhouette on transparent → our mark.
88
- badge: '/icons/badge-96.png',
88
+ badge: '/icons/badge-96.png?v=2',
89
89
  data: d.data || {},
90
90
  }));
91
91
  });
@@ -1,8 +1,17 @@
1
- // Cache-Control policy for the static web bundle. index.html and the service worker must NEVER be
2
- // HTTP-cached — an installed PWA would otherwise keep loading a stale shell (pointing at missing
3
- // hashed assets) or a stale SW. Everything else under dist carries a content hash in its name, so
4
- // it's safe to cache forever. Pure so it unit-tests on its own.
1
+ // Cache-Control policy for the static web bundle.
2
+ // • index.html + the service worker must NEVER be HTTP-cached — an installed PWA would otherwise
3
+ // keep loading a stale shell (pointing at missing hashed assets) or a stale service worker.
4
+ // • Vite build output under /assets/ carries a content hash in its filename, so the URL itself
5
+ // changes whenever the bytes change → safe to pin forever (immutable).
6
+ // • Everything else is a hand-authored public/ file served at a STABLE url whose bytes change in
7
+ // place across releases (icons, manifest.webmanifest, favicon, og cards, offline assets). Those
8
+ // must NOT be immutable — pinning them would freeze an old copy in every returning browser for a
9
+ // year (a changed app icon / manifest would never reach anyone who had visited before). They get
10
+ // `no-cache` = store but always revalidate; the ETag/Last-Modified express.static already sends
11
+ // make the recheck a cheap 304 when nothing changed, and an actual change is picked up at once.
12
+ // Pure so it unit-tests on its own.
5
13
  export function cacheControlFor(filePath) {
6
14
  if (filePath.endsWith('.html') || filePath.endsWith('/sw.js')) return 'no-store';
7
- return 'public, max-age=31536000, immutable';
15
+ if (/[\\/]assets[\\/]/.test(filePath)) return 'public, max-age=31536000, immutable';
16
+ return 'no-cache';
8
17
  }