First installed clips (idle, walk, happy) + manifest and a static collection page playing each sheet via CSS steps(). Backend SPA catch-all now serves a subdirectory's index.html, and the PWA service worker lets /avatars/* navigations through to the network. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
72 lines
3.0 KiB
TypeScript
72 lines
3.0 KiB
TypeScript
import { defineConfig } from "vite";
|
|
import react from "@vitejs/plugin-react";
|
|
import { VitePWA } from "vite-plugin-pwa";
|
|
import path from "node:path";
|
|
|
|
export default defineConfig({
|
|
// Build-time id used to bust the persisted React Query (IndexedDB) cache on
|
|
// every deploy, so a changed API response shape can't resurrect stale entries.
|
|
define: { __BUILD_ID__: JSON.stringify(String(Date.now())) },
|
|
resolve: {
|
|
alias: { "@": path.resolve(__dirname, "src") },
|
|
},
|
|
server: { host: true, port: 5180 },
|
|
plugins: [
|
|
react(),
|
|
VitePWA({
|
|
// The generated worker calls skipWaiting()/clientsClaim(), so a new build
|
|
// always activates and takes over the open page's fetches. What it never
|
|
// does is reload that page — src/pwa.ts does, once it sees the handover.
|
|
registerType: "autoUpdate",
|
|
// We register the worker ourselves from src/pwa.ts so we can poll for
|
|
// updates; suppress the <script src="/registerSW.js"> the plugin injects.
|
|
injectRegister: false,
|
|
includeAssets: ["icon.svg"],
|
|
manifest: {
|
|
name: "Claude Agent — context viewer",
|
|
short_name: "ai-agent",
|
|
description: "Viewer/editor for the homelab's CLAUDE.md files and skills.",
|
|
theme_color: "#0a0a0a",
|
|
background_color: "#0a0a0a",
|
|
display: "standalone",
|
|
orientation: "portrait",
|
|
start_url: "/",
|
|
icons: [
|
|
{ src: "/icon.svg", sizes: "any", type: "image/svg+xml", purpose: "any maskable" },
|
|
],
|
|
},
|
|
workbox: {
|
|
// The plugin only implies these for `injectRegister: "auto"`, and we
|
|
// register the worker ourselves. Without them a new build installs but
|
|
// parks in "waiting" forever, and nothing ever serves the new assets.
|
|
skipWaiting: true,
|
|
clientsClaim: true,
|
|
navigateFallback: "/index.html",
|
|
// /avatars/* are self-contained static pages (sprite collections), not
|
|
// SPA routes — the worker must let their navigations hit the network.
|
|
navigateFallbackDenylist: [/^\/api\//, /^\/avatars\//],
|
|
// Drop precaches from previous builds so an upgrade can't serve a mix of
|
|
// old and new hashed chunks.
|
|
cleanupOutdatedCaches: true,
|
|
// Cache the API bundle so the shell still has data on a cold offline start.
|
|
// `/api/events` is exempt: it is an endless SSE stream, and NetworkFirst
|
|
// would try to read the whole body into the cache. That read never
|
|
// finishes, which keeps the old worker alive and wedges every future
|
|
// update in "waiting" forever. Let it go straight to the network.
|
|
runtimeCaching: [
|
|
{
|
|
urlPattern: ({ url }) =>
|
|
url.pathname.startsWith("/api/") && url.pathname !== "/api/events",
|
|
handler: "NetworkFirst",
|
|
options: {
|
|
cacheName: "ai-agent-api",
|
|
networkTimeoutSeconds: 5,
|
|
expiration: { maxEntries: 32, maxAgeSeconds: 60 * 60 * 24 * 30 },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
}),
|
|
],
|
|
});
|