Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Build out the forge web UI

f09ef39efe44de693748053a7b596ef328fcf06b · Keenan Nemetz · 15d ago · parent 70b70264

Build out the forge web UI

Turn the web surface into a compact, server-rendered forge with HTMX as the
progressive dynamic layer.

- Split templates and assets into embedded files; add a tokenised dark theme,
  Lucide icons, and vendored HTMX.
- Add the repository Overview page and a separate chronological Activity feed
  with type filters and HTMX fragment updates.
- Finish the issue and change workflows: create pages, comments, close/reopen,
  labels and assignees, approval, discussion, line-anchored review comments,
  and HTMX list filters.
- Add syntax-highlighted blobs, contributor/statistics derivation, branch-aware
  code browsing, and a repository Runs list.
- Add a loopback-only development login for local write testing, with explicit
  configuration and listener checks.
- Extend tests for the new pages, fragments, labels, assignees, anchors, and
  development login.

server/web/icons_test.go +39

@@ -0,0 +1,39 @@
1 package web
2
3 import (
4 "io/fs"
5 "regexp"
6 "strings"
7 "testing"
8 )
9
10 func TestIconRendersInlineSVG(t *testing.T) {
11 got := string(icon("plus"))
12 if !strings.Contains(got, "<svg") || !strings.Contains(got, `aria-hidden="true"`) {
13 t.Fatalf("icon(plus) = %q", got)
14 }
15 if icon("no-such-icon") != "" {
16 t.Fatal("an unknown icon should render nothing")
17 }
18 }
19
20 // TestTemplatesOnlyUseKnownIcons catches a typo at test time instead of
21 // discovering an empty glyph on a page.
22 func TestTemplatesOnlyUseKnownIcons(t *testing.T) {
23 re := regexp.MustCompile(`\{\{icon "([^"]+)"\}\}`)
24 files, err := fs.Glob(templateFS, "templates/*.html")
25 if err != nil {
26 t.Fatal(err)
27 }
28 for _, name := range files {
29 body, err := fs.ReadFile(templateFS, name)
30 if err != nil {
31 t.Fatal(err)
32 }
33 for _, m := range re.FindAllStringSubmatch(string(body), -1) {
34 if _, ok := iconPaths[m[1]]; !ok {
35 t.Fatalf("%s uses unknown icon %q", name, m[1])
36 }
37 }
38 }
39 }