Build image / build-and-push (push) Successful in 7s
Render assistant chat output as GFM Markdown (tables, lists, code, headings), lazy-loaded so the ~150 KB renderer only ships when an assistant message shows. Hardened per review: no <img> (exfiltration-beacon guard), no raw HTML, error-boundary + stale-chunk recovery around the lazy chunk, GFM column alignment, and memoized parsing. Co-authored-by: Steve Dudenhoeffer <[email protected]>
59 lines
2.2 KiB
TypeScript
59 lines
2.2 KiB
TypeScript
import { defineConfig, loadEnv } from 'vite'
|
|
import react from '@vitejs/plugin-react'
|
|
import tailwindcss from '@tailwindcss/vite'
|
|
import { fileURLToPath, URL } from 'node:url'
|
|
|
|
// In dev, Vite serves the SPA with HMR and proxies /api to the Go backend. The
|
|
// backend listens on PANSY_PORT (default 8080); read it so the proxy target
|
|
// tracks an overridden port. The production build is embedded into the Go binary
|
|
// (see internal/webdist), so no proxy is involved there.
|
|
export default defineConfig(({ mode }) => {
|
|
const env = loadEnv(mode, process.cwd(), '')
|
|
const backendPort = env.PANSY_PORT || process.env.PANSY_PORT || '8080'
|
|
return {
|
|
plugins: [react(), tailwindcss()],
|
|
resolve: {
|
|
alias: {
|
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
|
},
|
|
},
|
|
server: {
|
|
port: 5173,
|
|
proxy: {
|
|
'/api': {
|
|
target: `http://127.0.0.1:${backendPort}`,
|
|
changeOrigin: true,
|
|
},
|
|
},
|
|
},
|
|
build: {
|
|
outDir: 'dist',
|
|
sourcemap: true,
|
|
rollupOptions: {
|
|
output: {
|
|
// The app-wide core goes in ONE cached vendor chunk (rarely changes, so
|
|
// it survives deploys while the tiny app chunk churns). react + react-dom
|
|
// + scheduler MUST stay together — splitting react-dom into its own chunk
|
|
// reorders module init across chunk boundaries and breaks React 19 at
|
|
// load ("Cannot set 'Activity' of undefined"). Everything else — the
|
|
// gesture engine, the assistant's markdown renderer + its ecosystem —
|
|
// rides with whatever imports it, so a lazily-loaded route/feature keeps
|
|
// it out of the eager first paint. The routes are code-split via
|
|
// React.lazy (router.tsx), where the real first-paint win is.
|
|
manualChunks(id) {
|
|
if (!id.includes('node_modules')) return undefined
|
|
if (
|
|
/[/\\]node_modules[/\\](react|react-dom|scheduler|@tanstack|zustand|zod|clsx|tailwind-merge)[/\\]/.test(
|
|
id,
|
|
)
|
|
) {
|
|
return 'vendor'
|
|
}
|
|
return undefined
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
})
|