diff --git a/admin.go b/admin.go
index 1441278..14220e5 100644
--- a/admin.go
+++ b/admin.go
@@ -24,9 +24,13 @@ const (
adminUsageHistoryPath = "/admin/api/usage/history"
)
-// adminCSP is the Content-Security-Policy header served with GET /admin
-// and the three /admin/api/* JSON routes (spec §4, v0.2; extended to the
-// JSON routes by a folded review item, 2026-08-20 review). It was
+// adminCSP is the Content-Security-Policy header served with GET /admin,
+// every GET /admin/assets/{hashedname} response (serveAdminAsset), and
+// the three /admin/api/* JSON routes — every /admin* response this
+// gateway ever sends (spec §4, v0.2; extended to the JSON routes by a
+// folded review item, 2026-08-20 review; extended to the asset route by
+// a later review sweep — see serveAdminAsset's own doc comment for why).
+// It was
// widened with `script-src 'unsafe-inline'`/`style-src 'unsafe-inline'`
// for the original vanilla-JS single-file page; the baked Vue build (Vue
// admin-panel task) needs neither: `vite build` emits index.html with
diff --git a/admin_test.go b/admin_test.go
index 4377db8..02af1d7 100644
--- a/admin_test.go
+++ b/admin_test.go
@@ -696,10 +696,33 @@ func TestAdminAssets_GeneratedFilePresent(t *testing.T) {
}
// adminInlineScriptRe matches every `)
+ if len(got) != 1 {
+ t.Fatalf("hasInlineScript = %v, want exactly 1 offender (data-src is not src=)", got)
+ }
+}
+
func truncateForTest(b []byte, n int) string {
if len(b) <= n {
return string(b)
diff --git a/webui/README.md b/webui/README.md
index 72da937..bdeaf3b 100644
--- a/webui/README.md
+++ b/webui/README.md
@@ -12,33 +12,47 @@ built, baked-in output, never `webui/` itself.
## Warning: `shadcn-vue add`/`init` regressions
-`components.json`'s `iconLibrary` and `font` fields are locked to
-`"lucide"` and `"inter"` — **not this project's real choices** (this
-project uses FontAwesome only, per `vue-web-development` house skill, and
-no external font at all). The shadcn-vue CLI's schema does not accept
-`"none"`/`"fontawesome"`/`"system"` for either field (checked directly:
-`node_modules/shadcn-vue/dist/preset-*.js`'s `PRESET_ICON_LIBRARIES` and
-`PRESET_FONTS` arrays — five fixed choices each, no escape hatch), and
-JSON supports no comment syntax to warn inline (the repo's own
-pre-commit JSON validator rejects JSONC comments — see `tsconfig.*.json`
-history). This warning is the only place the trap could be recorded.
+`components.json` has no `iconLibrary` or `font` key. **Do not add them
+back.** Their omission is the fix, not an oversight — read on before you
+"helpfully" restore either one.
-Both `npx shadcn-vue init` and `npx shadcn-vue add ` read
-these fields and **silently rewrite `src/assets/main.css`** — hit twice
-in this project's own history: `init` first, then a follow-up `add`
-call, both re-added a `@import url('https://fonts.googleapis.com/...')`
-line and switched the dark-mode `@custom-variant` back to a `.dark`-class
-toggle. If you ever run either command again:
+Earlier revisions of this file set `"iconLibrary": "lucide"` and
+`"font": "inter"`, both wrong for this project (FontAwesome only, per
+the `vue-web-development` house skill; no external font at all). Both
+`npx shadcn-vue init` and `npx shadcn-vue add ` read those two
+fields and, when set, rewrite `src/assets/main.css` — hit twice in this
+project's own history, `init` then a later `add`, both re-adding a
+`@import url('https://fonts.googleapis.com/...')` line and switching the
+dark-mode `@custom-variant` back to a `.dark`-class toggle.
-1. **Diff `src/assets/main.css` before committing.** Remove any
- re-added `@import url('https://fonts.googleapis...')` line (external
- font, blocked by the admin CSP's `style-src 'self'` anyway) and
- restore the `@custom-variant dark` removal (see the file's own header
- comment for why: `prefers-color-scheme`, not a class toggle).
-2. **Check the new component for a `lucide-vue-next`/`@lucide/vue`
- import** and swap it for the FontAwesome equivalent (see
- `src/components/ui/select/Select*.vue` for the pattern already
- applied there).
+The actual shadcn-vue config schema
+(`node_modules/shadcn-vue/dist/schema/index.js`'s `rawConfigSchema`)
+declares both fields as `z.string().optional()` — not an enum, contrary
+to an earlier, incorrect claim in this project's own review notes that
+the CLI schema forced one of five fixed values. The CLI's `init --font`/
+`--icon-library` flags do offer five fixed choices each, but that is a
+flag-parsing constraint, not a `components.json` schema constraint.
+Deleting both keys leaves them `undefined` after parse, a valid state:
+verified empirically, running `npx shadcn-vue add badge -o -y` against a
+`components.json` with both keys removed left `src/assets/main.css`
+byte-for-byte unchanged.
+
+That verification also surfaced a narrower, remaining trap: the same run
+still added `@lucide/vue` back to `package.json`'s dependencies (Badge
+itself does not use it — some components' registry entries list an icon
+package regardless of `iconLibrary`). So after any future `shadcn-vue
+add`/`init` call:
+
+1. **Check `git diff webui/package.json`** for a re-added
+ `lucide-vue-next`/`@lucide/vue` dependency, and remove it
+ (`npm uninstall`) if nothing in `src/` imports from it.
+2. **Check the new component's own file** for a
+ `lucide-vue-next`/`@lucide/vue` import and swap it for the
+ FontAwesome equivalent (see `src/components/ui/select/Select*.vue`
+ for the pattern already applied there).
+3. **Diff `src/assets/main.css` anyway** before committing, in case a
+ future shadcn-vue release starts reading a different config key for
+ the same font-injection step.
## Local development
diff --git a/webui/components.json b/webui/components.json
index 4282fc1..8203674 100644
--- a/webui/components.json
+++ b/webui/components.json
@@ -1,7 +1,6 @@
{
"$schema": "https://shadcn-vue.com/schema.json",
"style": "reka-nova",
- "font": "inter",
"typescript": true,
"tailwind": {
"config": "",
@@ -10,7 +9,6 @@
"cssVariables": true,
"prefix": ""
},
- "iconLibrary": "lucide",
"rtl": false,
"pointer": false,
"aliases": {
diff --git a/webui/generate.mjs b/webui/generate.mjs
index ba3d444..37d9091 100644
--- a/webui/generate.mjs
+++ b/webui/generate.mjs
@@ -80,7 +80,13 @@ function assertNoInlineAssets(html, sourceLabel) {
const scriptTagRe = /