diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-01 16:18:19 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-01 18:21:40 -0300 |
| commit | 151ea12251ed876aa87bb486da1bc337e25369e9 (patch) | |
| tree | 0363d90b5df6fc8b17b1737ef2793ba3825ba4d4 /vrsite/templates | |
| parent | 8ec4e135e318c7799b2576a8729493c063da4a97 (diff) | |
| download | notevi-151ea12251ed876aa87bb486da1bc337e25369e9.tar.gz notevi-151ea12251ed876aa87bb486da1bc337e25369e9.zip | |
Diffstat (limited to 'vrsite/templates')
| -rw-r--r-- | vrsite/templates/frag-note.tmpl | 57 | ||||
| -rw-r--r-- | vrsite/templates/layout.tmpl | 30 | ||||
| -rw-r--r-- | vrsite/templates/page-note.tmpl | 54 |
3 files changed, 129 insertions, 12 deletions
diff --git a/vrsite/templates/frag-note.tmpl b/vrsite/templates/frag-note.tmpl index 0c3182c..3b04e11 100644 --- a/vrsite/templates/frag-note.tmpl +++ b/vrsite/templates/frag-note.tmpl @@ -1,19 +1,26 @@ {{define "note"}}{{$n := .N}}{{$p := .P}}{{$away := and $n.Change $p.ChangeID (ne $n.Change $p.ChangeID)}} <article class="note am{{$n.Agent.Bit}}{{if $away}} away{{end}}" id="note-{{$n.ID}}" - data-agent="{{$n.Agent.Name}}" data-model="{{$n.Model}}" data-session="{{$n.Session}}" - data-kind="{{$n.Kind}}" data-file="{{$n.File}}" data-date="{{if not $n.At.IsZero}}{{$n.At.Format "2006-01-02"}}{{end}}" - data-start="{{$n.Start}}" data-end="{{$n.End}}"> + data-agent="{{$n.Agent.Name}}" data-model="{{$n.Model}}" data-session="{{$n.Session}}" + data-kind="{{$n.Kind}}" data-file="{{$n.File}}" data-date="{{if not $n.At.IsZero}}{{$n.At.Format "2006-01-02"}}{{end}}" + data-start="{{$n.Start}}" data-end="{{$n.End}}" data-reply-to="{{$n.ReplyTo}}"> <header> {{if $n.File}}<a href="{{$p.NoteHref $n}}" title="{{$n.File}}">{{base $n.File}}{{lines $n}}</a>{{end}} {{if $n.Kind}}<em>{{$n.Kind}}</em>{{end}} <b class="who">{{$n.Agent.Short}}</b> </header> - <p>{{$n.Text}}</p> + <div class="body">{{md $n.Text $p}}</div> <footer class="meta"> + {{with $p.PermaHref $n}}<a class="perma" href="{{.}}" + title="permalink — this note on its own page, with the code it is about">#{{$n.ID}}</a> + {{else}}<span class="perma self">#{{$n.ID}}</span>{{end}} {{if not $n.At.IsZero}}<time datetime="{{$n.At.Format "2006-01-02T15:04:05Z07:00"}}" title="{{$n.Ago}}">{{$n.When}}</time>{{end}} {{if $n.Model}}<span class="model">{{$n.Model}}{{if $n.Reason}}/{{$n.Reason}}{{end}}</span>{{end}} - {{if $n.Session}}<code class="sess" title="{{$n.Session}}">{{short $n.Session}}</code>{{end}} - {{with $p.ConvHref $n}}<a class="conv" href="{{.}}" title="open the conversation this note was written in">conversation</a>{{end}} + {{if $n.Session}}<code class="sess" title="{{$n.Session}}">{{short $n.Session}}</code>{{end}} + {{if $n.ReplyTo}}{{with $p.ReplyHref $n}}<a class="parent" href="{{.}}" + title="the note this explicitly replies to">reply to #{{$n.ReplyTo}}</a>{{else}}<span class="parent">reply to #{{$n.ReplyTo}}</span>{{end}}{{end}} + {{with $p.ThreadHref $n}}<a class="relations" href="{{.}}" title="notes replying to or mentioning this note"> + {{if $n.Replies}}↳ {{len $n.Replies}} repl{{if eq (len $n.Replies) 1}}y{{else}}ies{{end}}{{end}}{{if and $n.Replies $n.Mentions}} · {{end}}{{if $n.Mentions}}↶ {{len $n.Mentions}} mention{{if ne (len $n.Mentions) 1}}s{{end}}{{end}}</a>{{end}} + {{with $p.ConvHref $n}}<a class="conv" href="{{.}}" title="open the conversation this note was written in">conversation</a>{{end}} {{if $n.Change}} {{if $p.Live}}<a class="atchg{{if $away}} away{{end}}" href="{{$p.Href "change" $n.Change}}" title="{{if $away}}written at another change{{else}}written at this change{{end}}"><code>{{short $n.Change}}</code></a> @@ -30,6 +37,44 @@ {{define "note-added"}}{{if .File}}<span id="notecount" hx-swap-oob="true">{{len .File.Notes}}</span>{{end}}{{range .Notes}}{{template "note" (dict "N" . "P" $)}}{{end}}{{end}} +{{/* A child-note form appears below every full card in a focused thread. It + deliberately uses a normal POST: one append and one redirect rebuild the + entire DAG from the log, including shared mention paths. */}} +{{define "replyform"}}{{$n := .N}}{{$p := .P}} +<details class="replybox"> + <summary>reply to #{{$n.ID}}</summary> + <form class="notform replyform" method="post" action="{{$p.Href "notes"}}"> + <input type="hidden" name="reply_to" value="{{$n.ID}}"> + <input type="hidden" name="rev" value="{{$p.Rev}}"> + <input type="hidden" name="change" value="{{$p.ChangeID}}"> + <input type="hidden" name="scope" value="all"> + <input type="hidden" name="back" value="{{$p.Href "note" (printf "%d" $p.Note.ID)}}#discussion"> + <textarea name="text" rows="2" placeholder="reply… (⌘/ctrl+enter to post)" aria-label="reply to note {{$n.ID}}" required></textarea> + <div class="row end"> + <span class="meta">as <b>{{$p.User}}</b> at <code title="{{$p.ChangeID}}">{{short $p.ChangeID}}</code> → the log</span> + <button type="submit">reply</button> + </div> + </form> +</details> +{{end}} + +{{/* Recursive at arbitrary depth. A DAG join/cycle is a reference rather than + a second card, which keeps ids unique and recursion finite. */}} +{{define "threadnode"}}{{$x := .Node}}{{$p := .P}} +<div class="threadnode via-{{$x.Via}}"> + <div class="threadedge" aria-label="{{$x.Via}}"><span>{{if eq $x.Via "reply"}}↳{{else}}↶{{end}}</span></div> + <div class="threadcontent"> + {{if $x.Reference}} + <p class="threadref meta">{{$x.Via}} also reaches <a href="{{$p.Href "note" (printf "%d" $x.Note.ID)}}">note #{{$x.Note.ID}}</a></p> + {{else}} + {{template "note" (dict "N" $x.Note "P" $p)}} + {{if $p.Live}}{{template "replyform" (dict "N" $x.Note "P" $p)}}{{end}} + {{if $x.Children}}<div class="threadchildren">{{range $x.Children}}{{template "threadnode" (dict "Node" . "P" $p)}}{{end}}</div>{{end}} + {{end}} + </div> +</div> +{{end}} + {{/* the add-a-comment form; live pages only, since it writes to the log */}} {{define "noteform"}} <form class="notform" hx-post="{{.Href "notes"}}" hx-target="#notelist" hx-swap="afterbegin" diff --git a/vrsite/templates/layout.tmpl b/vrsite/templates/layout.tmpl index 9bc9ddd..268b5b6 100644 --- a/vrsite/templates/layout.tmpl +++ b/vrsite/templates/layout.tmpl @@ -4,12 +4,20 @@ <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>{{.Title}}</title> +<link rel="stylesheet" href="{{.Href "asset" "style.css"}}"> <script> +// the theme is worn before the first paint. A theme that is not one of the +// two the site was built with lives in themes.css, so pull that in too — +// from here, after style.css, so its :root block is the one that wins. {const d = document.documentElement, t = localStorage.getItem("vrsite-theme"); if (t && t !== "system") d.dataset.theme = t; + if (t && !/^(system|dark|light)$/.test(t)) { + const l = document.createElement("link"); + l.rel = "stylesheet"; l.id = "themescss"; l.href = "{{.Href "asset" "themes.css"}}"; + document.head.append(l); + } d.dataset.mode = localStorage.getItem("vrsite-mode") || "trace";} </script> -<link rel="stylesheet" href="{{.Href "asset" "style.css"}}"> {{if .Live}}<script src="{{.Href "asset" "htmx.min.js"}}"></script>{{end}} <script src="{{.Href "asset" "app.js"}}" defer></script> </head> @@ -35,11 +43,20 @@ <a href="{{.Href "files"}}">files</a>{{if .Live}} · <a href="{{.Href "jj"}}">jj</a>{{end}} </span> - <select id="themesel" aria-label="color scheme"> - <option value="system">system</option> - <option value="dark">dark</option> - <option value="light">light</option> - </select> + <div class="themepick" id="themepick" + data-js="{{.Href "asset" "themes.js"}}" data-css="{{.Href "asset" "themes.css"}}" + data-dark="{{.Site.Theme.Name}}" data-light="{{.Site.Light.Name}}" + data-darkid="{{.Site.Theme.ID}}" data-lightid="{{.Site.Light.ID}}"> + <button type="button" id="themebtn" aria-haspopup="listbox" aria-expanded="false" + title="theme — every one installed here (T)">theme</button> + <div class="thememenu" id="thememenu" hidden> + <input id="themefind" type="search" placeholder="filter themes…" aria-label="filter themes" + role="combobox" aria-controls="themelist" aria-autocomplete="list" + autocomplete="off" spellcheck="false"> + <ul id="themelist" role="listbox" aria-label="themes"></ul> + <p class="meta hint">↑↓ preview · enter keeps · esc puts it back</p> + </div> + </div> </footer> </nav> @@ -47,6 +64,7 @@ {{if .Err}}<p class="err">{{.Err}}</p>{{end}} {{if eq .Kind "file"}}{{template "page-file" .}} {{else if eq .Kind "notes"}}{{template "page-notes" .}} +{{else if eq .Kind "note"}}{{template "page-note" .}} {{else if eq .Kind "files"}}{{template "page-files" .}} {{else if eq .Kind "jj"}}{{template "page-jj" .}} {{else if eq .Kind "change"}}{{template "page-change" .}} diff --git a/vrsite/templates/page-note.tmpl b/vrsite/templates/page-note.tmpl new file mode 100644 index 0000000..8cd0e64 --- /dev/null +++ b/vrsite/templates/page-note.tmpl @@ -0,0 +1,54 @@ +{{/* One note, on its own — the page a shared permalink opens. + + It has to stand up for a reader who has never seen this site: what was + said, about which lines of which file, at which change, and the code + itself, right there. Everything else on the page is a way further in. */}} +{{define "page-note"}} +{{$n := .Note}} +<header class="filehead"> + <h1>{{if $n.File}}{{base $n.File}}<span class="lns">{{lines $n}}</span>{{else}}Note{{end}}</h1> + <p class="meta"> + note <b>#{{$n.ID}}</b> by <b>{{$n.Agent.Short}}</b>{{if $n.Kind}} · <em class="kind">{{$n.Kind}}</em>{{end}} + {{if not $n.At.IsZero}} · <time datetime="{{$n.At.Format "2006-01-02T15:04:05Z07:00"}}">{{$n.When}}</time> + <span class="ago">({{$n.Ago}})</span>{{end}} + {{if $n.Change}} · read at + {{if .Live}}<a href="{{.Href "change" $n.Change}}"><code>{{short $n.Change}}</code></a> + {{else}}<code title="{{$n.Change}}">{{short $n.Change}}</code>{{end}} + {{end}} + </p> + {{if ne $n.File (base $n.File)}}<p class="meta"><code>{{$n.File}}</code></p>{{end}} +</header> + +<section class="notepage"> + <div class="onenote">{{template "note" (dict "N" $n "P" $)}}</div> + + {{$open := .HasCode $n.File}} + {{if .Excerpt}} + <p class="meta exhead">lines {{.ExFrom}}–{{.ExTo}} of <code>{{$n.File}}</code>{{if $open}} · + <a href="{{.NoteHref $n}}">open the whole file →</a>{{end}}</p> + <div class="codewrap excerpt">{{.Excerpt}}</div> + {{else if .ExErr}} + <p class="meta stub">{{.ExErr}}</p> + {{else if $n.File}} + <p class="meta hint">this note is about the whole file{{if $open}} — + <a href="{{.NoteHref $n}}">open <code>{{base $n.File}}</code> →</a>{{end}}</p> + {{else}} + <p class="meta hint">this note is not pinned to a file</p> + {{end}} + + {{if .Live}}{{template "replyform" (dict "N" $n "P" $)}}{{end}} + + <section class="discussion" id="discussion"> + <h2>Replies & mentions + <span class="meta">{{len $n.Replies}} repl{{if eq (len $n.Replies) 1}}y{{else}}ies{{end}} · {{len $n.Mentions}} mention{{if ne (len $n.Mentions) 1}}s{{end}}</span> + </h2> + {{if .Thread.Children}} + <div class="threadchildren rootchildren">{{range .Thread.Children}}{{template "threadnode" (dict "Node" . "P" $)}}{{end}}</div> + {{else}} + <p class="meta hint">no notes reply to or mention this one yet</p> + {{end}} + </section> + + <p class="meta more"><a href="{{.Href "notes"}}">← every note in the log</a></p> +</section> +{{end}} |
