fix(templ): stop emitting literal { expr } text into attributes and body classes
Release / build-and-push (push) Successful in 3m31s

templ does not interpolate { expr } inside quoted attribute values — the
braces ship as literal text (AGENTS.md gotcha #1). Swept every templ
source for the pattern and fixed all of it:

Body theme classes: 22 page bodies rendered class="theme-{ user.Theme }"
literally, so every page first-painted the :root default (Tokyo Night)
and flashed when main.js applied the real theme. All now render
class={ "theme-" + user.Theme } server-side.

Attribute interpolations (26 across 8 files):
- api_explorer: initAPIExplorerDoc path/body/response and the Copy
  Request/Response buttons (invalid JS, silently dead)
- collections: book-selection :checked/@change/@click ids and four
  hx-get search URLs (collection_id={ collection.ID })
- conflicts: Resolve button id
- devices: Kobo sync-url/auth-token element ids (copy lookups only
  worked because the id and the lookup were the same literal)
- unlinked_books: search-matches/manual-link clicks and container ids
- queue: status/priority badge classes never matched their CSS
- dashboard: Restore-section tooltip text
- collection_rules/collections: collection icon color

Free text goes through the new templates.JSONArg (json.Marshal) —
templ.JSONString in this templ version returns (string, error) and
cannot compose in attribute expressions; uuids via device.ID.String().
Includes regen-only drift from running templ generate (error-path
normalization) on files with no source change.
This commit is contained in:
John O'Keefe committed 2026-10-18 12:08:20 -04:00
1 parent d5bb874651
commit b89eac22e3
58 files changed
+4473 -3618

No files matched your search

+5 -5
View File
@@ -13,7 +13,7 @@ templ Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []P
<link href="/static/style.css" rel="stylesheet"/>
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg"/>
</head>
<body class="theme-{ user.Theme }" x-data="devices" x-init="setupEventDelegation()">
<body class={ "theme-" + user.Theme } x-data="devices" x-init="setupEventDelegation()">
@Header(user, "/devices")
<div class="mx-auto container px-4 sm:px-6 lg:px-8 py-8">
<div class="mb-8">
@@ -105,13 +105,13 @@ templ Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []P
<div class="flex space-x-2">
<input
type="text"
id="sync-url-{ device.ID }"
id={ "sync-url-" + device.ID.String() }
readonly
value={ baseURL + "/api/sync/kobo/" + device.AuthToken }
class="input text-xs font-mono"
/>
<button
@click="copyToClipboard(document.getElementById('sync-url-{ device.ID }').value, 'Kobo sync URL')"
@click={ "copyToClipboard(document.getElementById('sync-url-" + device.ID.String() + "').value, 'Kobo sync URL')" }
class="btn btn-primary text-xs px-3"
>
@Icon("copy", "h-4 w-4")
@@ -127,13 +127,13 @@ templ Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []P
<div class="flex space-x-2">
<input
type="text"
id="auth-token-{ device.ID }"
id={ "auth-token-" + device.ID.String() }
readonly
value={ device.AuthToken }
class="input text-xs font-mono"
/>
<button
@click="copyToClipboard(document.getElementById('auth-token-{ device.ID }').value, 'Auth token')"
@click={ "copyToClipboard(document.getElementById('auth-token-" + device.ID.String() + "').value, 'Auth token')" }
class="btn btn-primary text-xs px-3"
>
@Icon("copy", "h-4 w-4")