A CLI-first git forge.

cli forge git self-hosted

https://gitbay.org

Commit e278948b45

e278948b45ed675b9da3799bb89de836e3f2a954

parent: 14a47d9502

Verified · cmc ci/build: success

cmc <hello@cleberg.net> · 2026-08-25T20:12:14Z

Class-based syntax highlighting with light and dark palettes

chroma emits classes instead of inline colors; the stylesheet appends
two generated palettes — friendly by default, github-dark under the
same prefers-color-scheme query the rest of the site uses — with the
site's --code-bg kept as the background in both. Ref #10
e2e/design_test.go +10
@@ -101,6 +101,16 @@ func TestReadmeRelativeLinks(t *testing.T) {
101101 if _, body := inst.get(t, "/alice/site/blob/main/img/logo.png"); !strings.Contains(body, `<img src="/alice/site/raw/main/img/logo.png"`) {
102102 t.Errorf("blob image preview missing:\n%s", body)
103103 }
104 // Highlighting is class-based so the palette follows the color scheme:
105 // no inline colors on code, and the stylesheet carries both palettes.
106 if _, body := inst.get(t, "/alice/site/blob/main/README.md"); !strings.Contains(body, `class="chroma"`) ||
107 strings.Contains(body, "style=\"color") {
108 t.Errorf("highlighting not class-based:\n%.2000s", body)
109 }
110 if _, css := inst.get(t, "/static/style.css"); strings.Count(css, "/* Background */") < 2 ||
111 !strings.Contains(css, ".chroma, .bg { background: var(--code-bg)") {
112 t.Error("stylesheet missing dual syntax palettes")
113 }
104114 // Explore rows carry topics, license, and updated date.
105115 inst.ssh(t, aliceKey, "", "repo", "topics", "add", "alice/site", "web")
106116 // Bare 0BSD grant (no notice-retention clause), wrapped mid-sentence.
internal/httpd/routes.go +3 −3
@@ -129,9 +129,9 @@ func (s *Server) Handler() http.Handler {
129129
130130 // securityHeaders sets defensive response headers on every reply. The CSP
131131 // is strict where it can be: no scripts at all (the UI needs none), no
132// plugins, no embedding. Inline styles are allowed because chroma emits
133// inline style attributes on highlighted code and label chips carry their
134// color inline. Images may load from anywhere so external README images
132// plugins, no embedding. Inline styles are allowed because label chips
133// carry their color inline (chroma is class-based so the syntax palette
134// can follow the color scheme). Images may load from anywhere so external README images
135135 // still render; they are the one thing a reader-facing forge can't police
136136 // without a proxy.
137137 func (s *Server) securityHeaders(next http.Handler) http.Handler {
internal/httpd/web.go +20 −4
@@ -53,6 +53,7 @@ func (s *Server) siteName() string {
5353 func (s *Server) stylesheet(w http.ResponseWriter, r *http.Request) {
5454 w.Header().Set("Content-Type", "text/css; charset=utf-8")
5555 w.Write(web.StyleCSS)
56 w.Write(chromaCSS)
5657 }
5758
5859 func (s *Server) favicon(w http.ResponseWriter, r *http.Request) {
@@ -734,25 +735,40 @@ type numberedLine struct {
734735 Text string
735736 }
736737
738// chromaFormatter emits class-based markup (no inline colors), so the
739// stylesheet can swap palettes with the color scheme.
740var chromaFormatter = html.New(html.WithClasses(true),
741 html.WithLineNumbers(true), html.LineNumbersInTable(false),
742 html.WithLinkableLineNumbers(true, "L"))
743
737744 func highlight(filePath string, data []byte) template.HTML {
738745 lexer := lexers.Match(filePath)
739746 if lexer == nil {
740747 lexer = lexers.Fallback
741748 }
742 style := styles.Get("friendly")
743 formatter := html.New(html.WithLineNumbers(true), html.LineNumbersInTable(false),
744 html.WithLinkableLineNumbers(true, "L"))
745749 iterator, err := lexer.Tokenise(nil, string(data))
746750 if err != nil {
747751 return template.HTML("<pre>" + template.HTMLEscapeString(string(data)) + "</pre>")
748752 }
749753 var buf bytes.Buffer
750 if err := formatter.Format(&buf, style, iterator); err != nil {
754 if err := chromaFormatter.Format(&buf, styles.Get("friendly"), iterator); err != nil {
751755 return template.HTML("<pre>" + template.HTMLEscapeString(string(data)) + "</pre>")
752756 }
753757 return template.HTML(buf.String())
754758 }
755759
760// chromaCSS is both syntax palettes: light by default, dark under the same
761// media query the rest of the stylesheet uses. The site's --code-bg stays
762// the background either way.
763var chromaCSS = func() []byte {
764 var buf bytes.Buffer
765 chromaFormatter.WriteCSS(&buf, styles.Get("friendly"))
766 buf.WriteString("\n@media (prefers-color-scheme: dark) {\n")
767 chromaFormatter.WriteCSS(&buf, styles.Get("github-dark"))
768 buf.WriteString("}\n.chroma, .bg { background: var(--code-bg) !important; }\n")
769 return buf.Bytes()
770}()
771
756772 func (s *Server) raw(w http.ResponseWriter, r *http.Request) {
757773 p, ok := s.repoFor(w, r, r.PathValue("ref"))
758774 if !ok {