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.
Highlight with Prism in a worker and gzip text responses
306f36fcef89c145abcd0c5331453d0b179f37ce · Keenan Nemetz · 12d ago · parent e5542ac6
Highlight with Prism in a worker and gzip text responses
The code browser now names a blob's language and lets the browser colour it.
Chroma is gone: server/web/highlight.go is an extension table, and
static/prism-worker.js runs Prism — every language Prism ships — off the main
thread. The server sends escaped lines with their numbers and L# anchors, so a
page is complete and scrollable before any script runs and the worker only swaps
the contents of each line cell. The table covers 186 languages; a file it does
not claim stays plain rather than being coloured by a guess.
The bundle those grammars make is 545 KB, so the instance now gzips text
responses itself instead of relying on a proxy, and static assets carry a
content ETag so a browser revalidates with a 304 rather than re-downloading the
bundle on every code page. The raw-download route opts out with
Cache-Control: no-transform because it promises an exact Content-Length.
This also makes JavaScript a requirement rather than a nicety: the label and
assignee token inputs lose their noscript save buttons and their comma-separated
text fallback, and the docs say so.
Measured: release binary 26.08 MB -> 22.70 MB; /r/{repo}/code 11.1 KB -> 2.6 KB
over the wire; the Prism bundle 545 KB -> 206 KB gzipped, fetched once.
scripts/vendor-prism.py regenerates the bundle from Prism's components.json
require graph, and --check fails if the committed assets drift.
server/issues_changes_test.go +5−5
| @@ -175,7 +175,7 @@ func TestIssueLabelsCanBeSetAndEditedInTheBrowser(t *testing.T) { | |||
| 175 | 175 | ||
| 176 | 176 | resp, body := f.browserPost(t, "/r/"+f.repo+"/issues", cookie, url.Values{ | |
| 177 | 177 | "csrf": {sess.CSRF}, "title": {"Labels from the browser"}, "body": {"body\n"}, | |
| 178 | "label": {"bug", "web"}, "labels": {"perf, docs"}, | ||
| 178 | "label": {"bug", "web", "perf", "docs"}, | ||
| 179 | 179 | }) | |
| 180 | 180 | if resp.StatusCode != http.StatusSeeOther { | |
| 181 | 181 | t.Fatalf("create issue: %d\n%s", resp.StatusCode, body) | |
| @@ -206,7 +206,7 @@ func TestIssueLabelsCanBeSetAndEditedInTheBrowser(t *testing.T) { | |||
| 206 | 206 | } | |
| 207 | 207 | ||
| 208 | 208 | resp, body = f.browserPost(t, "/r/"+f.repo+"/issues/"+id+"/labels", cookie, url.Values{ | |
| 209 | "csrf": {sess.CSRF}, "label": {"bug"}, "labels": {"ux"}, | ||
| 209 | "csrf": {sess.CSRF}, "label": {"bug", "ux"}, | ||
| 210 | 210 | }) | |
| 211 | 211 | if resp.StatusCode != http.StatusSeeOther { | |
| 212 | 212 | t.Fatalf("edit labels: %d\n%s", resp.StatusCode, body) | |
| @@ -233,7 +233,7 @@ func TestIssueAssigneesCanBeSetAndEditedInTheBrowser(t *testing.T) { | |||
| 233 | 233 | ||
| 234 | 234 | resp, body := f.browserPost(t, "/r/"+f.repo+"/issues", cookie, url.Values{ | |
| 235 | 235 | "csrf": {sess.CSRF}, "title": {"Assignees from the browser"}, "body": {"body\n"}, | |
| 236 | "assignee": {"wilbur"}, "assignees": {"keenan"}, | ||
| 236 | "assignee": {"wilbur", "keenan"}, | ||
| 237 | 237 | }) | |
| 238 | 238 | if resp.StatusCode != http.StatusSeeOther { | |
| 239 | 239 | t.Fatalf("create issue: %d\n%s", resp.StatusCode, body) | |
| @@ -323,7 +323,7 @@ func TestIssueSidebarFragmentsUpdateWithHtmx(t *testing.T) { | |||
| 323 | 323 | ||
| 324 | 324 | // Labels: the response is the fragment, not a page and not a redirect. | |
| 325 | 325 | resp, body = f.htmxPost(t, "/r/"+f.repo+"/issues/"+id+"/labels", cookie, url.Values{ | |
| 326 | "csrf": {sess.CSRF}, "label": {"bug"}, "labels": {"ux"}, | ||
| 326 | "csrf": {sess.CSRF}, "label": {"bug", "ux"}, | ||
| 327 | 327 | }) | |
| 328 | 328 | if resp.StatusCode != http.StatusOK { | |
| 329 | 329 | t.Fatalf("htmx labels: %d\n%s", resp.StatusCode, body) | |
| @@ -334,7 +334,7 @@ func TestIssueSidebarFragmentsUpdateWithHtmx(t *testing.T) { | |||
| 334 | 334 | } | |
| 335 | 335 | ||
| 336 | 336 | // Assignees: the replacement fragment seeds the token input and keeps the | |
| 337 | // save control. | ||
| 337 | // token-change trigger. | ||
| 338 | 338 | resp, body = f.htmxPost(t, "/r/"+f.repo+"/issues/"+id+"/assignees", cookie, url.Values{ | |
| 339 | 339 | "csrf": {sess.CSRF}, "assignee": {"wilbur"}, | |
| 340 | 340 | }) | |