From 81bf0016b3a776732bba430872ce5719f3251e70 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Mon, 4 Nov 2024 20:00:57 +0100 Subject: Add heading_template to gemtext middleware --- README.md | 11 +++++++++++ http/handlers/gemtext.go | 34 +++++++++++++++++++++++++++++++++- internal/gemtext/gemtext.go | 38 +++++++++++++++++++++++++++----------- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 6293bd8..829157a 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,17 @@ The template will be rendered with these extra data fields: * `.Body`: A string containing all rendered HTML DOM elements. +**heading_template** + +Path to a template which will be used for rendering headings. If not given then +headings will be rendered with appropriate HTML header tags. + +The template will be rendered with these extra data fields: + +* `.Level`: Which level of heading is being rendered, 1, 2, or 3. + +* `.Text`: The text of the heading. + **link_template** Path to a template which will be used for rendering links. If not given then diff --git a/http/handlers/gemtext.go b/http/handlers/gemtext.go index bc617f4..7f12799 100644 --- a/http/handlers/gemtext.go +++ b/http/handlers/gemtext.go @@ -64,6 +64,20 @@ type Gemtext struct { // TemplatePath string `json:"template"` + // Path to a template which will be used for rendering headings. If not + // given then headings will be rendered with appropriate HTML header tags. + // + // The template will be rendered with these extra data fields: + // + // ##### `.Level` + // + // Which level of heading is being rendered, 1, 2, or 3. + // + // ##### `.Text` + // + // The text of the heading. + HeadingTemplatePath string `json:"heading_template"` + // Path to a template which will be used for rendering links. If not given // then links will be rendered using an anchor tag wrapped in a paragraph // tag. @@ -181,9 +195,23 @@ func (g *Gemtext) ServeHTTP( Req: r, RespHeader: templates.WrappedHeader{Header: rec.Header()}, } + + parser gemtext.HTMLTranslator ) - parser := gemtext.HTMLTranslator{} + if g.HeadingTemplatePath != "" { + parser.RenderHeading = func(w io.Writer, level int, text string) error { + payload := struct { + *templates.TemplateContext + Level int + Text string + }{ + ctx, level, text, + } + + return g.render(w, ctx, osFS, g.HeadingTemplatePath, payload) + } + } if g.LinkTemplatePath != "" { parser.RenderLink = func(w io.Writer, url, label string) error { @@ -258,6 +286,10 @@ func gemtextParseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, if !h.Args(&g.TemplatePath) { return nil, h.ArgErr() } + case "heading_template": + if !h.Args(&g.HeadingTemplatePath) { + return nil, h.ArgErr() + } case "link_template": if !h.Args(&g.LinkTemplatePath) { return nil, h.ArgErr() diff --git a/internal/gemtext/gemtext.go b/internal/gemtext/gemtext.go index 76a990e..2b8cf77 100644 --- a/internal/gemtext/gemtext.go +++ b/internal/gemtext/gemtext.go @@ -14,6 +14,11 @@ import ( // HTMLTranslator is used to translate a gemtext file into equivalent HTML DOM // elements. type HTMLTranslator struct { + // RenderHeading, if given can be used to override how headings are + // rendered. The level indicates which heading level is being rendered: 1, + // 2, or 3. + RenderHeading func(w io.Writer, level int, text string) error + // RenderLink, if given, can be used to override how links are rendered. RenderLink func(w io.Writer, url, label string) error } @@ -45,7 +50,7 @@ func (t HTMLTranslator) Translate(src io.Reader) (HTML, error) { if writeErr != nil { return } - fmt.Fprintf(w, fmtStr, args...) + _, writeErr = fmt.Fprintf(w, fmtStr, args...) } loop: @@ -109,25 +114,36 @@ loop: if t.RenderLink == nil { write("

%s

\n", urlStr, label) } else { - if err := t.RenderLink(w, urlStr, label); err != nil { - return HTML{}, fmt.Errorf( - "rendering link %q (label:%q): %w", urlStr, label, err, - ) - } + writeErr = t.RenderLink(w, urlStr, label) } case strings.HasPrefix(line, "###"): - write("

%s

\n", sanitizeText(line[3:])) + text := sanitizeText(line[3:]) + if t.RenderHeading == nil { + write("

%s

\n", text) + } else { + writeErr = t.RenderHeading(w, 3, text) + } case strings.HasPrefix(line, "##"): - write("

%s

\n", sanitizeText(line[2:])) + text := sanitizeText(line[2:]) + if t.RenderHeading == nil { + write("

%s

\n", text) + } else { + writeErr = t.RenderHeading(w, 2, text) + } case strings.HasPrefix(line, "#"): - line = sanitizeText(line[1:]) + text := sanitizeText(line[1:]) if title == "" { - title = line + title = text + } + + if t.RenderHeading == nil { + write("

%s

\n", text) + } else { + writeErr = t.RenderHeading(w, 1, text) } - write("

%s

\n", line) case strings.HasPrefix(line, ">"): write("
%s
\n", sanitizeText(line[1:])) -- cgit v1.2.3