docs(agents): rendering-ownership rule — templ renders, JS decides what shows
Codifies the standing rule discussed for the templ/JS split: content derived from server data is rendered by templ and refreshed via templ partials (htmx.ajax), pure client-local state may render in TS/Alpine, web/src is TypeScript-only, and inline <script> in templ files requires a pre-paint justification (theme_flash_guard.templ only). Lists the sanctioned JS islands (reader, Chart.js, toasts, password validation). Also refreshes gotcha #1: the 'harmlessly masked' aside is stale — the quoted-attribute sweep fixed every occurrence; the note now points at templates.JSONArg for free text.
This commit is contained in:
1 parent
b89eac22e3
commit
1fdf306f2d
1 file changed
+33
-3
@@ -4,6 +4,34 @@ Standing rules for any agent or developer working in this repo.
|
|||||||
Project-wide context lives in the sibling repos' AGENTS.md files
|
Project-wide context lives in the sibling repos' AGENTS.md files
|
||||||
(../bookhoard-app/AGENTS.md is the Android client's).
|
(../bookhoard-app/AGENTS.md is the Android client's).
|
||||||
|
|
||||||
|
## Rendering ownership (templ vs TypeScript) — ALWAYS apply
|
||||||
|
|
||||||
|
The point of templ + htmx + Alpine is: the server renders the UI, and
|
||||||
|
JavaScript is kept minimal. The rule that keeps it that way:
|
||||||
|
|
||||||
|
> **JavaScript decides what shows. templ decides what it looks like.**
|
||||||
|
|
||||||
|
- Content derived from **server data** (DB rows, search results, match
|
||||||
|
lists, conflict cards, any list/card/badge/table) is rendered by
|
||||||
|
templ. When it changes client-side, fetch a **templ partial** from an
|
||||||
|
SSR route and swap it in (`htmx.ajax(...)`), never rebuild it from a
|
||||||
|
JSON payload with `innerHTML` string templates.
|
||||||
|
- **Pure functions of client-local state** (open/closed panels, tabs,
|
||||||
|
password strength from a typed value, toasts) may render in
|
||||||
|
TS/Alpine — no server round trip needed.
|
||||||
|
- `web/src` is **TypeScript only** — no new `.js` source files.
|
||||||
|
Vendored third-party and vite build output under `web/static` are
|
||||||
|
exempt.
|
||||||
|
- Inline `<script>` in a `.templ` file needs a reason a bundled module
|
||||||
|
cannot do the job (pre-paint execution). Currently sanctioned:
|
||||||
|
`theme_flash_guard.templ` only.
|
||||||
|
|
||||||
|
Sanctioned JS islands (engines, not UI renderers — exempt from the
|
||||||
|
above): the reader (foliate-js + `web/src/reader/`), Chart.js graphs in
|
||||||
|
stats, toasts, password validation. Anything else that renders app UI
|
||||||
|
in TypeScript is a defect; if a templ twin of it exists, one of the two
|
||||||
|
must be deleted.
|
||||||
|
|
||||||
## Library visibility (family-lens rule) — ALWAYS apply
|
## Library visibility (family-lens rule) — ALWAYS apply
|
||||||
|
|
||||||
Per-user library visibility (`library_visibility` table, default-ALLOW:
|
Per-user library visibility (`library_visibility` table, default-ALLOW:
|
||||||
@@ -96,9 +124,11 @@ of a server bug: stop and flag the user.
|
|||||||
values** — it emits the literal text. `x-init="f('{ col.ID }')"`
|
values** — it emits the literal text. `x-init="f('{ col.ID }')"`
|
||||||
shipped a request to `/api/collections/{ collection.ID }` found by
|
shipped a request to `/api/collections/{ collection.ID }` found by
|
||||||
the request logs; the whole attribute value must BE an expression:
|
the request logs; the whole attribute value must BE an expression:
|
||||||
`x-init={ "f('" + col.ID + "')" }`. Note
|
`x-init={ "f('" + col.ID + "')" }`. (Every occurrence of this bug —
|
||||||
`class="theme-{ user.Theme }"` has always been literal (harmlessly
|
including `class="theme-{ user.Theme }"` on 22 page bodies and 26
|
||||||
masked by the JS theme engine).
|
attribute interpolations — was swept and fixed in 2026-10; when
|
||||||
|
adding attributes with dynamic values, the whole value is an
|
||||||
|
expression, and free text goes through `templates.JSONArg`.)
|
||||||
2. **SettingsRegistry caches**: writes must go through the settings
|
2. **SettingsRegistry caches**: writes must go through the settings
|
||||||
API (Reload on save). Direct SQL writes to system_settings leave
|
API (Reload on save). Direct SQL writes to system_settings leave
|
||||||
the cache stale until the next API-path write — never "preview"
|
the cache stale until the next API-path write — never "preview"
|
||||||
|
|||||||
Reference in new issue
Block a user