diff options
| author | Gabriel Schneider <[email protected]> | 2026-07-24 11:39:36 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-07-30 15:18:55 -0300 |
| commit | cb05c363045bf0e7af5858f6d7bc26f026fa9d70 (patch) | |
| tree | c3e89426ff044ffe339dd3a77e7f3b7115cba636 /vrsite/page.go | |
| download | notevi-cb05c363045bf0e7af5858f6d7bc26f026fa9d70.tar.gz notevi-cb05c363045bf0e7af5858f6d7bc26f026fa9d70.zip | |
Diffstat (limited to 'vrsite/page.go')
| -rw-r--r-- | vrsite/page.go | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/vrsite/page.go b/vrsite/page.go new file mode 100644 index 0000000..71a8343 --- /dev/null +++ b/vrsite/page.go @@ -0,0 +1,172 @@ +// page.go — what a template gets, and how links are formed. +// +// The same templates render two ways: as a static export (relative links, +// ".html" suffixes, no server-side features) and as the live web app +// (absolute routes, htmx-driven filters, comments and jj queries). Pages carry +// Live so a template can ask for the difference in one place. +package main + +import ( + "html/template" + "net/url" + "strconv" + "strings" +) + +type page struct { + Site *Site + Title string + Kind string // index | file | files | notes | jj | change + Root string // static: relative prefix; live: "/" + Live bool + Rev string // revision being browsed ("" = the site change) + ChangeID string // resolved change id for Rev + Current string // current file path, for the tree + User string // who web comments are attributed to + + Tree template.HTML + File *File + + Notes []*Note + Filter NoteFilter + Facets *Facets + Sessions []*Session + Compact bool + Window string + + JJ *JJView + Change *Change + Err string +} + +// JJView is one rendered jj query: what was asked, and what came back. +type JJView struct { + Kind string // log | op + Revset string + AtOp string + Limit int + Rows []Row + Err string +} + +func (p *page) revQuery(first bool) string { + if p.Rev == "" || p.Rev == "@" { + return "" + } + sep := "&" + if first { + sep = "?" + } + return sep + "rev=" + url.QueryEscape(p.Rev) +} + +// Href maps a logical destination to a URL for this page's mode. Kinds the +// static export has no page for return "", and templates guard on .Live. +func (p *page) Href(kind string, arg ...string) string { + a := "" + if len(arg) > 0 { + a = arg[0] + } + if p.Live { + switch kind { + case "": + return "/" + p.revQuery(true) + case "notes": + return "/notes" + case "files": + return "/files" + p.revQuery(true) + case "tree": + return "/tree" + p.revQuery(true) + case "jj": + return "/jj" + case "code": + return "/code/" + escPath(a) + p.revQuery(true) + case "change": + return "/change/" + a + case "asset": + return "/" + a + } + return "/" + } + switch kind { + case "": + return p.Root + "index.html" + case "notes": + return p.Root + "notes.html" + case "files": + return p.Root + "files.html" + case "tree": + return p.Root + "tree.html" + case "code": + return p.Root + "code/" + a + ".html" + case "asset": + return p.Root + a + } + return "" +} + +// codeLink is the tree's per-file linker for this page's mode. +func (p *page) codeLink() func(string) string { + return func(path string) string { return p.Href("code", path) } +} + +func escPath(p string) string { + parts := strings.Split(p, "/") + for i, s := range parts { + parts[i] = url.PathEscape(s) + } + return strings.Join(parts, "/") +} + +// NoteHref points at the noted line, or the note list when it is unpinned. +func (p *page) NoteHref(n *Note) string { + if n.File == "" { + return p.Href("notes") + } + h := p.Href("code", n.File) + if n.Start > 0 { + h += "#L" + strconv.Itoa(n.Start) + } + return h +} + +func newTemplates() *template.Template { + funcs := template.FuncMap{ + "short": short, + "band": func(a, b uint8) bool { return b != 0 && a&b != 0 }, + "lines": func(n *Note) string { + if n.Start <= 0 { + return "whole file" + } + if n.End > n.Start { + return ":" + strconv.Itoa(n.Start) + "-" + strconv.Itoa(n.End) + } + return ":" + strconv.Itoa(n.Start) + }, + "base": func(p string) string { + if i := strings.LastIndex(p, "/"); i >= 0 { + return p[i+1:] + } + return p + }, + "dict": func(kv ...any) map[string]any { + m := map[string]any{} + for i := 0; i+1 < len(kv); i += 2 { + m[kv[i].(string)] = kv[i+1] + } + return m + }, + } + tpl, err := template.New("layout.tmpl").Funcs(funcs).ParseFS(tfs, "templates/*.tmpl") + if err != nil { + fatal("%v", err) + } + return tpl +} + +func short(s string) string { + if len(s) > 8 { + return s[:8] + } + return s +} |
