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.

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 })