Side-by-side comparison surfaced that several restyle classes silently generated NO CSS (confirmed against the compiled .next CSS) — invisible to tests because unit tests assert class strings, not computed styles, and visual baselines were captured from the broken build. Root causes fixed: - rounded-radius-button (51) / rounded-radius-squircle (3) generated nothing → every pill/button/chip was border-radius:0. Tailwind v4 maps --radius-button to 'rounded-button', not 'rounded-radius-button'. Replaced with rounded-full / rounded-[17px]. - duration-normal/fast/slow (58) generated nothing → transitions had easing but 0 duration (snapped). Replaced with the token-arbitrary form duration-(--duration-normal). (Also fixed an over-replace that double-wrapped var(--duration-normal) inside animate-[…] values.) - HomeScreen.tsx was the OLD emoji version — the R3 rewrite was never committed. Restored: brand Chrome/Slack squircle tiles, coral GradientGlyph icons, 56px tiles with press-scale, and the frosted glass dock (bg-white/[0.07] backdrop-blur-[40px] saturate-150). Targeted gaps from the feedback: - raised surface --color-surface-warm-muted #1f1f1f → #1a1a1a (composer, chips, tabs) - welcome chips: max-w-[422px] gap-3 → centered 2×2 grid - composer input + rail nav labels → 14px - DeviceSizeToggle: Full/Tablet/Phone text → lucide monitor/tablet/ smartphone icons (aria-label keeps the radio names) - SW cache bumped v1→v2 so redeployed clients purge the stale bundle - LeftRail drawer: ref-during-render → prev-state pattern (lint) Compiled CSS now emits .rounded-full, transition-duration:var(--duration- normal), border-radius:17px, and shadow-dock-capsule. Button/Avatar unit tests updated to the real class names. 83 unit tests green. Co-Authored-By: Claude Fable 5 <[email protected]>
61 lines
1.8 KiB
JavaScript
61 lines
1.8 KiB
JavaScript
// Clawmates service worker (§16 PWA). Hand-rolled on purpose: ~50 lines we
|
|
// fully control beats a build-tool plugin. Strategy:
|
|
// /api/** -> NEVER touched (approvals + SSE must be live)
|
|
// /_next/static/** -> cache-first (content-hashed, immutable)
|
|
// navigations -> network-first, cache fallback for offline shell
|
|
const STATIC_CACHE = "tc-static-v2";
|
|
const PAGE_CACHE = "tc-pages-v2";
|
|
|
|
self.addEventListener("install", () => {
|
|
self.skipWaiting();
|
|
});
|
|
|
|
self.addEventListener("activate", (event) => {
|
|
event.waitUntil(
|
|
(async () => {
|
|
const keep = [STATIC_CACHE, PAGE_CACHE];
|
|
for (const key of await caches.keys()) {
|
|
if (!keep.includes(key)) await caches.delete(key);
|
|
}
|
|
await self.clients.claim();
|
|
})(),
|
|
);
|
|
});
|
|
|
|
self.addEventListener("fetch", (event) => {
|
|
const url = new URL(event.request.url);
|
|
if (url.origin !== self.location.origin) return;
|
|
if (url.pathname.startsWith("/api/")) return;
|
|
|
|
if (url.pathname.startsWith("/_next/static/")) {
|
|
event.respondWith(
|
|
(async () => {
|
|
const cache = await caches.open(STATIC_CACHE);
|
|
const hit = await cache.match(event.request);
|
|
if (hit) return hit;
|
|
const response = await fetch(event.request);
|
|
if (response.ok) cache.put(event.request, response.clone());
|
|
return response;
|
|
})(),
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (event.request.mode === "navigate") {
|
|
event.respondWith(
|
|
(async () => {
|
|
const cache = await caches.open(PAGE_CACHE);
|
|
try {
|
|
const response = await fetch(event.request);
|
|
if (response.ok) cache.put(event.request, response.clone());
|
|
return response;
|
|
} catch {
|
|
const hit = await cache.match(event.request);
|
|
if (hit) return hit;
|
|
throw new Error("offline and not cached");
|
|
}
|
|
})(),
|
|
);
|
|
}
|
|
});
|