vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Search every issue, change and comment from the top bar
6c03e6d3243d31a82210cc7bc689ab3e28226b6c · Keenan Nemetz · 10d ago · parent 389317ee
Search every issue, change and comment from the top bar The top bar's search now opens a command palette instead of a text field. The dialog asks the same index and visibility rule as /search, so private repositories stay out, and it answers with a short list plus a link to the full results. A button that looks like the old input launches it, labelled with the platform's shortcut; Ctrl K or Cmd K works anywhere. The bar's contents also line up with the page column now, and Home is marked current only on the landing page rather than on every repository page.
server/nav_test.go +28−20
| @@ -9,33 +9,41 @@ import ( | |||
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | 11 | // TestHomeIsTheGlobalDestination: the bar needs a text destination for the | |
| 12 | // landing page. The emblem links there too, but a mark is not a label. Home marks | ||
| 13 | // itself current wherever the repository list is the context: the landing page | ||
| 14 | // and a repository page alike. | ||
| 12 | // landing page. The emblem links there too, but a mark is not a label. Home is | ||
| 13 | // current only on the landing page: a repository page has its own tabs saying | ||
| 14 | // where the reader is, so the global bar must not claim they are on Home. | ||
| 15 | 15 | func TestHomeIsTheGlobalDestination(t *testing.T) { | |
| 16 | 16 | t.Parallel() | |
| 17 | 17 | f := newFixture(t) | |
| 18 | 18 | f.publish(f.repo) | |
| 19 | 19 | ||
| 20 | for _, path := range []string{"/", "/r/" + f.repo} { | ||
| 21 | code, body := f.get(path) | ||
| 22 | if code != http.StatusOK { | ||
| 23 | t.Fatalf("%s = %d", path, code) | ||
| 24 | } | ||
| 25 | if !strings.Contains(body, ">Home</a>") { | ||
| 26 | t.Fatalf("%s has no Home link:\n%s", path, body) | ||
| 27 | } | ||
| 28 | if !strings.Contains(body, `class="active" href="/" aria-current="page"`) { | ||
| 29 | t.Fatalf("%s does not mark Home current:\n%s", path, body) | ||
| 30 | } | ||
| 31 | if !strings.Contains(body, `class="icon"`) { | ||
| 32 | t.Fatalf("%s renders the Home link without an icon", path) | ||
| 33 | } | ||
| 20 | code, body := f.get("/") | ||
| 21 | if code != http.StatusOK { | ||
| 22 | t.Fatalf("/ = %d", code) | ||
| 23 | } | ||
| 24 | if !strings.Contains(body, ">Home</a>") { | ||
| 25 | t.Fatalf("/ has no Home link:\n%s", body) | ||
| 26 | } | ||
| 27 | if !strings.Contains(body, `class="active" href="/" aria-current="page"`) { | ||
| 28 | t.Fatalf("/ does not mark Home current:\n%s", body) | ||
| 29 | } | ||
| 30 | if !strings.Contains(body, `class="icon"`) { | ||
| 31 | t.Fatalf("/ renders the Home link without an icon") | ||
| 32 | } | ||
| 33 | |||
| 34 | // A repository page is not the landing page. Its own header tabs carry the | ||
| 35 | // context, so Home must not take that role from them. | ||
| 36 | code, body = f.get("/r/" + f.repo) | ||
| 37 | if code != http.StatusOK { | ||
| 38 | t.Fatalf("/r/%s = %d", f.repo, code) | ||
| 39 | } | ||
| 40 | if strings.Contains(body, `class="active" href="/"`) { | ||
| 41 | t.Fatalf("repository page marks Home current:\n%s", body) | ||
| 34 | 42 | } | |
| 35 | 43 | ||
| 36 | // Activity is a different destination, so Home stops being current — and | ||
| 37 | // Activity takes over, which is what makes the bar say where you are. | ||
| 38 | code, body := f.get("/activity") | ||
| 44 | // Activity is a different destination, so it takes over, which is what makes | ||
| 45 | // the bar say where you are. | ||
| 46 | code, body = f.get("/activity") | ||
| 39 | 47 | if code != http.StatusOK { | |
| 40 | 48 | t.Fatalf("/activity = %d", code) | |
| 41 | 49 | } | |