Files
ai-agent/frontend/vite.config.ts
Gabriel Vidal 9a2627f1ea feat(ai-agent): /avatars/hemp-henry/ animated sprite collection page
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>
2026-07-18 02:34:32 +02:00

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 },
},
},
],
},
}),
],
});