Micro-fix from re-review:
1. webui/components.json: deleted the font/iconLibrary keys instead of
picking a value. Re-review checked shadcn-vue's actual parse schema
(node_modules/shadcn-vue/dist/schema/index.js's rawConfigSchema):
both fields are z.string().optional(), not an enum - the five-choice
enum lives only on the init CLI's --font/--icon-library flags, not on
the components.json schema. The previous commit's "schema forbids a
fix" rationale was wrong; corrected here. Verified empirically, not
just by reading the schema: ran a real `npx shadcn-vue add badge -o
-y` against components.json with both keys removed - src/assets/
main.css came back byte-for-byte unchanged (previously this exact
class of call re-added a Google Fonts import and the .dark-class
toggle). That same run did still re-add @lucide/vue to package.json's
dependencies (some components' registry entries list an icon package
regardless of iconLibrary) - reverted before committing, and
webui/README.md's warning section rewritten to explain the omission
(so nobody "helpfully" restores the keys), fix the schema claim, and
note the narrower remaining package.json trap.
2. admin.go:27-28: adminCSP's doc comment now names all three set-sites
(GET /admin, every GET /admin/assets/*, the three JSON routes) -
previously only mentioned the page and JSON routes, dropped after the
asset route gained the same header in the prior fix round.
3. Both inline-script guards (webui/generate.mjs's assertNoInlineAssets,
admin_test.go's hasInlineScript) tightened from a bare \bsrc\s*=
pattern to (^|\s)src\s*= - \b alone matches inside "data-src=" too
(the "-" before "src" is a non-word character, so \b sits right
before "src"), which would wrongly treat a data-src/nosrc attribute
as the real src= that makes a <script> external. New Go test
TestHasInlineScript_DataSrcIsNotExternal: <script data-src="x">
is still flagged as inline.
No webui/src/ change this round - components.json isn't bundled, and
generate.mjs's edit only touches its own build-time guard, not the
Go-source-generation path. admin_assets_gen.go NOT regenerated
(confirmed via a throwaway `node generate.mjs` + gofmt -w run: output
is byte-identical to what's already committed).
Vue 3 + TypeScript + Pinia + Tailwind v4 + shadcn-vue + Chart.js admin
panel source under webui/, built with Vite. Views: Overview (providers,
redis/cache/retry status, model aliases), Usage (per-user/per-group/total
tables with tokensIn/tokensOut/cost + limits), Charts (scope selector,
req/tokens(stacked in-out)/cost metric tabs, 24h/30d/12mo window
switcher, fed by GET /admin/api/usage/history). Pinia stores: auth
(sessionStorage key gate, x-api-key header, 401/403 re-prompt),
dashboard (5s poll), history (fetch-on-selection + 30s refresh).
Design: deliberate neutral warm-neutral palette with a signature teal
accent (no default Inter/purple-gradient/glassmorphism), real
typographic hierarchy via the platform UI/mono font stacks (zero
external assets - no Google Fonts), dark+light via prefers-color-scheme
(no class toggle, no theme switcher). Charts: zero-based bars, honest
axes, Okabe-Ito colorblind-safe series, tokens-in/out as a
distinguishable stacked pair.
vite.config.ts: base '/admin/' (matches admin.go's route layout) and
build.modulePreload.polyfill:false (no inline bootstrap script, so the
tightened admin CSP needs no 'unsafe-inline'). Code-split: Chart.js
(ChartsView) loads lazily, not in the initial bundle.
generate.mjs reads dist/ after `vite build` and writes
../admin_assets_gen.go (not committed by this change) - base64-encodes
every asset, decoded once at package-var init; documented rationale in
the script's own header comment.
Hygiene: webui/go.mod walls webui/ off from the repo root's `go build`/
`vet`/`test ./...` (npm's node_modules bundles a stray .go file that
would otherwise be swept in); tools/yaegi-check/main.go excludes webui/
from its GOPATH copy for the same reason. webui/ export-ignored
(.gitattributes) from release source tarballs; webui/node_modules/
gitignored. tsconfig.app.json/tsconfig.node.json's JSONC comments
dropped (TS itself is fine with either; the global pre-commit JSON
validator is not) - no config values changed.