diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-23 20:46:17 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-07-23 20:46:17 +0200 |
| commit | 0b47572bc0addc5f59ebac3e70278ea435097565 (patch) | |
| tree | a04a3a3afb2c074b2f714f396506dc5bb4f5cbca /internal | |
| parent | 734eb4f3aec4e3c10064c7af4bd8e33c64ad07a7 (diff) | |
| download | lectio-0b47572bc0addc5f59ebac3e70278ea435097565.tar.gz lectio-0b47572bc0addc5f59ebac3e70278ea435097565.zip | |
web: theme control boxes, one default version, drop search bar; traditional drops pl + all=readings-only
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/readings/readings.go | 26 | ||||
| -rw-r--r-- | internal/readings/readings_test.go | 23 | ||||
| -rw-r--r-- | internal/web/server.go | 84 | ||||
| -rw-r--r-- | internal/web/server_test.go | 27 | ||||
| -rw-r--r-- | internal/web/static/base.css | 2 | ||||
| -rw-r--r-- | internal/web/templates/index.html | 9 | ||||
| -rw-r--r-- | internal/web/templates/lookup.html | 20 |
7 files changed, 66 insertions, 125 deletions
diff --git a/internal/readings/readings.go b/internal/readings/readings.go index 886c1eb..82d2fb9 100644 --- a/internal/readings/readings.go +++ b/internal/readings/readings.go @@ -6,6 +6,7 @@ package readings import ( "fmt" + "strings" "github.com/lukaszkasprzak/lectio/internal/config" "github.com/lukaszkasprzak/lectio/internal/liturgy" @@ -56,8 +57,12 @@ func Load(cfg config.Config, opts Options) ([]liturgy.Section, error) { } // filterParts keeps only the gospel when !all (PartID "ewangelia" modern or -// "evangelium" traditional), otherwise keeps sections cfg.PartShown allows -// (a section with an empty PartID is always kept). Pure and network-free. +// "evangelium" traditional). When all and the lectionary is traditional, it +// keeps only the scripture readings (isTraditionalReading) -- dropping +// Introit/Kolekta/Graduale/Offertorium/Sekreta/Prefacja/Komunia/Pokomunia +// and any commemoration, none of which are readings; the "new" lectionary +// path is unchanged, keeping sections cfg.PartShown allows (a section with +// an empty PartID is always kept). Pure and network-free. func filterParts(secs []liturgy.Section, cfg config.Config, all bool) []liturgy.Section { var out []liturgy.Section for _, s := range secs { @@ -67,9 +72,26 @@ func filterParts(secs []liturgy.Section, cfg config.Config, all bool) []liturgy. } continue } + if cfg.Lectionary == "traditional" { + if isTraditionalReading(s.PartID) { + out = append(out, s) + } + continue + } if s.PartID == "" || cfg.PartShown(cfg.Lectionary, s.PartID) { out = append(out, s) } } return out } + +// isTraditionalReading reports whether partID (a lowercased missalemeum +// section id) is a scripture reading -- the gospel, or an epistle/lesson -- +// rather than Introit/Kolekta/Graduale/Offertorium/Sekreta/Prefacja/ +// Komunia/Pokomunia or a commemoration. +func isTraditionalReading(partID string) bool { + return partID == "evangelium" || + strings.HasPrefix(partID, "lectio") || + strings.HasPrefix(partID, "epistola") || + strings.HasPrefix(partID, "prophetia") +} diff --git a/internal/readings/readings_test.go b/internal/readings/readings_test.go index fd355ff..20df5e0 100644 --- a/internal/readings/readings_test.go +++ b/internal/readings/readings_test.go @@ -55,6 +55,29 @@ func TestFilterPartsAll(t *testing.T) { } } +func TestFilterPartsTraditionalAllReadingsOnly(t *testing.T) { + secs := []liturgy.Section{ + {PartID: "introitus", Heading: "Introit"}, + {PartID: "oratio", Heading: "Kolekta"}, + {PartID: "lectio", Heading: "Lekcja"}, + {PartID: "graduale", Heading: "GraduaĆ"}, + {PartID: "evangelium", Heading: "Ewangelia"}, + {PartID: "offertorium", Heading: "Ofiarowanie"}, + {PartID: "secreta", Heading: "Sekreta"}, + {PartID: "communio", Heading: "Komunia"}, + {PartID: "postcommunio", Heading: "Pokomunia"}, + } + cfg := config.Config{Lectionary: "traditional"} + got := filterParts(secs, cfg, true) + + if len(got) != 2 { + t.Fatalf("got %d sections, want 2: %+v", len(got), got) + } + if got[0].PartID != "lectio" || got[1].PartID != "evangelium" { + t.Errorf("got %+v, want [lectio evangelium] in order", got) + } +} + func TestLoadModernRoutes(t *testing.T) { html, err := os.ReadFile("../liturgy/testdata/2026-07-22.html") if err != nil { diff --git a/internal/web/server.go b/internal/web/server.go index c7e645d..76a5d3b 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -1,12 +1,11 @@ // This file wires the package's render helpers (RenderReadings, Themes, // themeCSS, the embedded static/templates FS) into an HTTP server: the full -// page (GET /), the HTMX reading-pane partial (GET /readings), a passage -// lookup partial (GET /lookup), a theme stylesheet endpoint (GET -// /theme.css) and the embedded static assets (GET /static/...). +// page (GET /), the HTMX reading-pane partial (GET /readings), a theme +// stylesheet endpoint (GET /theme.css) and the embedded static assets (GET +// /static/...). package web import ( - "bytes" "fmt" "html/template" "io" @@ -19,7 +18,6 @@ import ( "strings" "time" - "github.com/lukaszkasprzak/lectio/internal/bible" "github.com/lukaszkasprzak/lectio/internal/config" "github.com/lukaszkasprzak/lectio/internal/liturgy" "github.com/lukaszkasprzak/lectio/internal/readings" @@ -39,7 +37,6 @@ func NewServer(cfg config.Config) http.Handler { mux.HandleFunc("GET /{$}", indexHandler(cfg)) mux.HandleFunc("GET /readings", readingsHandler(cfg)) - mux.HandleFunc("GET /lookup", lookupHandler(cfg)) mux.HandleFunc("GET /theme.css", themeCSSHandler(cfg)) staticSub, err := fs.Sub(staticFS, "static") @@ -76,12 +73,14 @@ func shiftDate(date string, days int) string { } // requestVersions returns the versions requested via one or more repeated -// ?v= query params, falling back to cfg.Versions when none were given. +// ?v= query params, falling back to a single cfg.DefaultVersion (not the +// full cfg.Versions set) when none were given, so a plain visit checks +// exactly one version box. func requestVersions(cfg config.Config, r *http.Request) []string { if vs, ok := r.URL.Query()["v"]; ok && len(vs) > 0 { return vs } - return append([]string(nil), cfg.Versions...) + return []string{cfg.DefaultVersion} } // queryBool reads a truthy/falsy query param ("1"/"true"/"on"/"yes" vs. @@ -138,15 +137,20 @@ func resolveQuery(cfg config.Config, r *http.Request) (date, lectionary string, } // loadSections runs the readings router for one request: date/lectionary -// override cfg, all controls part filtering, and (when cfg.Offline) -// versions is swapped via render.OfflineVersions before being handed back -// to the caller for rendering -- so the caller's column labels always match -// what was actually loadable. +// override cfg, all controls part filtering, and versions is swapped via +// render.OfflineVersions -- when cfg.Offline (any lectionary needs the +// network-free set), or when lectionary is "traditional" (pl is the +// niedziela.pl modern scrape, meaningless for missalemeum) -- before being +// handed back to the caller for rendering, so the caller's column labels +// always match what was actually loadable. func loadSections(cfg config.Config, lectionary, date string, all bool, versions []string) ([]liturgy.Section, []string, error) { cfg.Lectionary = lectionary if cfg.Offline { versions = render.OfflineVersions(versions) } + if lectionary == "traditional" { + versions = render.OfflineVersions(versions) + } secs, err := readings.Load(cfg, readings.Options{ Date: date, Offline: cfg.Offline, @@ -165,8 +169,6 @@ type indexData struct { Theme string Display string Mono bool - Ref string - Lookup template.HTML Reading template.HTML } @@ -209,12 +211,6 @@ func indexHandler(cfg config.Config) http.HandlerFunc { themeOpts = append(themeOpts, themeOpt{Name: name, Selected: name == theme}) } - ref := r.URL.Query().Get("ref") // echoed into the lookup box on a shared/bookmarked link - var lookup template.HTML - if ref != "" { - lookup = renderLookup(ref, versions) - } - data := indexData{ Date: date, PrevDate: shiftDate(date, -1), @@ -226,8 +222,6 @@ func indexHandler(cfg config.Config) http.HandlerFunc { Theme: theme, Display: display, Mono: queryBool(r, "mono", cfg.WebMono), - Ref: ref, - Lookup: lookup, Reading: reading, } @@ -266,52 +260,6 @@ func renderOrError(secs []liturgy.Section, versions []string, lectionary, displa return RenderReadings(secs, versions, lectionary, display) } -// lookupColumn is what templates/lookup.html ranges over: one per requested -// version. -type lookupColumn struct { - Version string - Verses []bible.Verse - Missing []string -} - -// renderLookup renders the passage-lookup fragment for ref (looked up as-is -// -- already English/kjv-style, e.g. "J 20:1" -- against each of versions -// via bible.Lookup directly; no Polish->English conversion, that only -// applies to a section's own citation via render.GatherVersion, not a -// free-typed lookup) against each requested version. Shared by lookupHandler -// (the HTMX partial) and indexHandler (so a bookmarked/shared "/?ref=..." -// link shows the same result instead of an empty pane). ref is trimmed of -// surrounding whitespace here so both routes treat a whitespace-only ref -// identically; an empty (or now-empty) ref renders nothing, matching -// lookupHandler's previous no-op behavior. -func renderLookup(ref string, versions []string) template.HTML { - ref = strings.TrimSpace(ref) - if ref == "" { - return "" - } - cols := make([]lookupColumn, 0, len(versions)) - for _, v := range versions { - verses, missing := bible.Lookup(v, ref) - cols = append(cols, lookupColumn{Version: v, Verses: verses, Missing: missing}) - } - - var buf bytes.Buffer - if err := tmpl.ExecuteTemplate(&buf, "lookup.html", cols); err != nil { - return template.HTML(`<p class="error">` + template.HTMLEscapeString(err.Error()) + `</p>`) - } - return template.HTML(buf.String()) -} - -// lookupHandler serves the passage-lookup fragment via renderLookup (see -// its doc comment for the lookup semantics). -func lookupHandler(cfg config.Config) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - ref := r.URL.Query().Get("ref") // renderLookup trims whitespace - w.Header().Set("Content-Type", "text/html; charset=utf-8") - io.WriteString(w, string(renderLookup(ref, requestVersions(cfg, r)))) - } -} - // themeCSSHandler serves one theme's stylesheet: the requested name, or // (unknown/invalid) cfg.WebTheme, or (that also unknown) the built-in // default -- so an unrecognized ?name= degrades to a working theme instead diff --git a/internal/web/server_test.go b/internal/web/server_test.go index 30c8fbf..47c38e9 100644 --- a/internal/web/server_test.go +++ b/internal/web/server_test.go @@ -64,17 +64,6 @@ func TestServer(t *testing.T) { } }) - t.Run("lookup", func(t *testing.T) { - rec := httptest.NewRecorder() - srv.ServeHTTP(rec, httptest.NewRequest("GET", "/lookup?ref=J+20:1&v=wuj", nil)) - if rec.Code != http.StatusOK { - t.Fatalf("status = %d, want 200", rec.Code) - } - if !strings.Contains(rec.Body.String(), "20:1") { - t.Errorf("lookup result missing verse: %q", rec.Body.String()) - } - }) - t.Run("theme.css", func(t *testing.T) { rec := httptest.NewRecorder() srv.ServeHTTP(rec, httptest.NewRequest("GET", "/theme.css?name=benedictines", nil)) @@ -172,22 +161,6 @@ func TestServer(t *testing.T) { } }) - t.Run("index page seeds lookup results from ?ref=", func(t *testing.T) { - rec := httptest.NewRecorder() - srv.ServeHTTP(rec, httptest.NewRequest("GET", "/?ref=J+20:1&v=wuj", nil)) - if rec.Code != http.StatusOK { - t.Fatalf("status = %d, want 200", rec.Code) - } - body := rec.Body.String() - if !strings.Contains(body, `id="lookup-results"`) { - t.Fatalf("body missing lookup-results container: %q", body) - } - i := strings.Index(body, `id="lookup-results"`) - if !strings.Contains(body[i:], "20:1") { - t.Errorf("lookup-results not populated from ?ref=: %q", body[i:min(i+400, len(body))]) - } - }) - t.Run("index page seeds display select from cfg", func(t *testing.T) { rec := httptest.NewRecorder() srv.ServeHTTP(rec, httptest.NewRequest("GET", "/?date=2026-07-22&v=wuj", nil)) diff --git a/internal/web/static/base.css b/internal/web/static/base.css index 59c67aa..f31dc64 100644 --- a/internal/web/static/base.css +++ b/internal/web/static/base.css @@ -82,6 +82,8 @@ body.mono { border-width: 1px; border-style: solid; border-radius: 0; /* no rounded chrome */ + background: var(--bg); + color: var(--fg); } /* Reading pane */ diff --git a/internal/web/templates/index.html b/internal/web/templates/index.html index 8c88779..cc56a09 100644 --- a/internal/web/templates/index.html +++ b/internal/web/templates/index.html @@ -1,7 +1,6 @@ {{/* index.html renders the full lectio-web page: top-bar controls (date, lectionary, version checkboxes, all/gospel toggle, display select, - theme select), a passage-lookup form, and the pre-rendered reading pane - (#pane). + theme select) 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 @@ -72,12 +71,6 @@ <label class="mono-toggle"><input type="checkbox" {{if .Mono}}checked{{end}} onchange="document.body.classList.toggle('mono', this.checked)"> mono</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> diff --git a/internal/web/templates/lookup.html b/internal/web/templates/lookup.html deleted file mode 100644 index c8bc2a7..0000000 --- a/internal/web/templates/lookup.html +++ /dev/null @@ -1,20 +0,0 @@ -{{/* 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}} |
