diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-23 14:39:31 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-23 14:39:31 +0200 |
| commit | 4de7f7dca7485c9a6c94f6f86a53d01a974fce54 (patch) | |
| tree | bcdc1ea366a068310e460804c4a5f04df3ae20f4 /internal/web/templates | |
| parent | b72b81c410c161797eb525a6231b0517b4def993 (diff) | |
| download | lectio-4de7f7dca7485c9a6c94f6f86a53d01a974fce54.tar.gz lectio-4de7f7dca7485c9a6c94f6f86a53d01a974fce54.zip | |
web: HTMX server + lectio-web binary
NewServer wires B1's RenderReadings/Themes/themeCSS/embedded static+
templates FS into an http.ServeMux: GET / (full page), GET /readings
(HTMX reading-pane fragment), GET /lookup (bible.Lookup passage search
fragment), GET /theme.css (theme stylesheet, falling back through
cfg.WebTheme to the built-in default on an unknown name), GET /static/.
Run listens on cfg.WebPort (0 = OS-picked free port), prints the URL,
best-effort opens a browser, then serves. cmd/lectio-web is the binary
entry point (config.Load -> web.Run).
Fold-in from the B1 review: hardened themeCSS's name guard to an
explicit ^[A-Za-z0-9_-]+$ allowlist (the old filepath.Base/ContainsAny
check let ".." through), plus guard-rejection and HTML-escaping
regression tests -- B2 is what makes /theme.css?name=<raw> reachable
from the network, so it owns closing this out.
Diffstat (limited to 'internal/web/templates')
| -rw-r--r-- | internal/web/templates/index.html | 72 | ||||
| -rw-r--r-- | internal/web/templates/lookup.html | 20 |
2 files changed, 92 insertions, 0 deletions
diff --git a/internal/web/templates/index.html b/internal/web/templates/index.html new file mode 100644 index 0000000..4dd580f --- /dev/null +++ b/internal/web/templates/index.html @@ -0,0 +1,72 @@ +{{/* index.html renders the full lectio-web page: top-bar controls (date, + lectionary, version checkboxes, all/gospel toggle, theme select), a + passage-lookup form, and the pre-rendered reading pane (#pane). + + The controls form (#controls) carries hx-get="/readings" and fires on + any "change" bubbling up from its fields (htmx auto-includes an + hx-get element's enclosing form fields as the request's query + params -- no hx-include needed). The date +/- buttons are separate + hx-get elements nested in the same form so their own field values are + included too; they override "date" via hx-vals. The theme <select> + has no server round trip: it swaps the #theme <link>'s href directly. */}} +<!doctype html> +<html lang="pl"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>lectio</title> +<link rel="stylesheet" href="/static/base.css"> +<link id="theme" rel="stylesheet" href="/theme.css?name={{.Theme}}"> +<script src="/static/htmx.min.js"></script> +</head> +<body> +<div class="page"> + + <form id="controls" class="controls" hx-get="/readings" hx-target="#pane" hx-trigger="change"> + <span class="date-nav"> + <button type="button" hx-get="/readings" hx-target="#pane" + hx-vals='{"date":"{{.PrevDate}}"}'>←</button> + <input type="date" name="date" value="{{.Date}}"> + <button type="button" hx-get="/readings" hx-target="#pane" + hx-vals='{"date":"{{.NextDate}}"}'>→</button> + </span> + + <label>lekcjonarz + <select name="lectionary"> + <option value="new" {{if eq .Lectionary "new"}}selected{{end}}>nowy</option> + <option value="traditional" {{if eq .Lectionary "traditional"}}selected{{end}}>tradycyjny</option> + </select> + </label> + + {{range .VersionOpts}} + <label><input type="checkbox" name="v" value="{{.Code}}" {{if .Checked}}checked{{end}}> {{.Code}}</label> + {{end}} + + <label>zakres + <select name="all"> + <option value="0" {{if not .All}}selected{{end}}>Ewangelia</option> + <option value="1" {{if .All}}selected{{end}}>wszystkie części</option> + </select> + </label> + </form> + + <label class="theme-picker">motyw + <select id="theme-select" + onchange="document.getElementById('theme').href='/theme.css?name='+encodeURIComponent(this.value)"> + {{range .ThemeOpts}} + <option value="{{.Name}}" {{if .Selected}}selected{{end}}>{{.Name}}</option> + {{end}} + </select> + </label> + + <form id="lookup-form" class="controls" hx-get="/lookup" hx-target="#lookup-results" hx-include="#controls"> + <input type="text" name="ref" placeholder="np. J 20:1" value="{{.Ref}}"> + <button type="submit">szukaj</button> + </form> + <div id="lookup-results">{{.Lookup}}</div> + + <div id="pane">{{.Reading}}</div> + +</div> +</body> +</html> diff --git a/internal/web/templates/lookup.html b/internal/web/templates/lookup.html new file mode 100644 index 0000000..c8bc2a7 --- /dev/null +++ b/internal/web/templates/lookup.html @@ -0,0 +1,20 @@ +{{/* lookup.html renders the passage-lookup fragment: one .column per + requested version, each version's matched bible.Verse rows plus any + sub-refs the corpus had no entry for. Structurally mirrors + readings.html's .columns/.column/.block classes so theme CSS restyles + both panes the same way. */}} +{{if .}} +<div class="columns"> + {{range .}} + <div class="column"> + <h3 class="version-label">{{.Version}}</h3> + {{range .Verses}} + <p class="block verse"><span class="vnum">{{.Chapter}}:{{.Verse}}</span> {{.Text}}</p> + {{end}} + {{if .Missing}} + <p class="block error">brak: {{range $i, $m := .Missing}}{{if $i}}, {{end}}{{$m}}{{end}}</p> + {{end}} + </div> + {{end}} +</div> +{{end}} |
