Commit b1661d0

HPCesia <me@hpcesia.com>
2026-07-02 02:10:14
Add raw file download for all files and raw button on blob page
1 parent 5beb863
pkg/templates/blob.gohtml
@@ -27,8 +27,6 @@
           word-wrap: normal;
           tab-size: 4;
           font-family: var(--font-family-mono), monospace;
-          background: var(--code-block-bg);
-          color: var(--code-block-color);
         }
 
         pre > code {
@@ -40,11 +38,6 @@
           font-size: var(--code-font-size);
         }
 
-        .chroma {
-          background: var(--code-block-bg);
-          color: var(--code-block-color);
-        }
-
         .border {
           border: 1px solid var(--c-border);
           border-top: none;
pkg/templates/header.gohtml
@@ -24,6 +24,9 @@
                     {{ end }}
                 </nav>
             {{ end }}
+            {{ if .RawHref }}
+                <a href="{{ .RawHref }}" class="raw-button">Raw</a>
+            {{ end }}
             <button class="goto-top" onclick="window.scrollTo({top: 0, behavior: 'smooth'})">
                 <svg aria-hidden="true" focusable="false" width="16" height="16">
                     <use xlink:href="#arrow-top"></use>
pkg/templates/templates.go
@@ -87,6 +87,7 @@ type HeaderParams struct {
 	Ref         git.Ref
 	Header      string
 	Breadcrumbs []Breadcrumb
+	RawHref     string
 }
 
 type Breadcrumb struct {
blob.go
@@ -37,11 +37,9 @@ func generateBlobs(files []git.Blob, params Params) error {
 	}
 
 	// Use a temporary formatter to render CSS once
-	var rawCSS strings.Builder
-	if err := html.New(formatterOptions...).WriteCSS(&rawCSS, style); err != nil {
+	if err := html.New(formatterOptions...).WriteCSS(&css, style); err != nil {
 		return err
 	}
-	css.WriteString(stripChromaBase(rawCSS.String()))
 
 	dirsSet := links.BuildDirSet(files)
 	filesSet := links.BuildFileSet(files)
@@ -119,6 +117,23 @@ func generateBlobs(files []git.Blob, params Params) error {
 					}
 					rootHref := strings.Repeat("../", depth+2)
 
+					rawPath := filepath.Join(params.OutputDir, "raw", params.Ref.DirName(), blob.Path)
+					if err := os.MkdirAll(filepath.Dir(rawPath), 0o755); check(err) {
+						return
+					}
+
+					rf, err := os.Create(rawPath)
+					if check(err) {
+						return
+					}
+
+					if _, err := rf.Write(data); check(err) {
+						return
+					}
+					_ = rf.Close()
+
+					rawHref := filepath.Join(rootHref, "raw", params.Ref.DirName(), blob.Path)
+
 					if isMarkdown(blob.Path) {
 						var b bytes.Buffer
 						if err := md.Convert([]byte(content), &b); check(err) {
@@ -150,6 +165,7 @@ func generateBlobs(files []git.Blob, params Params) error {
 							HeaderParams: templates.HeaderParams{
 								Ref:         params.Ref,
 								Breadcrumbs: breadcrumbs(params.Name, blob.Path, true),
+								RawHref:     rawHref,
 							},
 							Blob:    blob,
 							Content: template.HTML(contentHTML),
@@ -173,27 +189,10 @@ func generateBlobs(files []git.Blob, params Params) error {
 							}
 							contentHTML = template.HTML(b.String())
 
-						} else if isImg {
-
-							rawPath := filepath.Join(params.OutputDir, "raw", params.Ref.DirName(), blob.Path)
-							if err := os.MkdirAll(filepath.Dir(rawPath), 0o755); check(err) {
-								return
-							}
-
-							rf, err := os.Create(rawPath)
-							if check(err) {
-								return
-							}
-							defer func() {
-								_ = rf.Close()
-							}()
-
-							if _, err := rf.Write(data); check(err) {
-								return
-							}
+						}
 
-							relativeRawPath := filepath.Join(rootHref, "raw", params.Ref.DirName(), blob.Path)
-							contentHTML = template.HTML(fmt.Sprintf(`<img src="%s" alt="%s" />`, relativeRawPath, blob.FileName))
+						if isImg {
+							contentHTML = template.HTML(fmt.Sprintf(`<img src="%s" alt="%s" />`, rawHref, blob.FileName))
 						}
 
 						err = templates.BlobTemplate.ExecuteTemplate(f, "layout.gohtml", templates.BlobParams{
@@ -211,6 +210,7 @@ func generateBlobs(files []git.Blob, params Params) error {
 							HeaderParams: templates.HeaderParams{
 								Ref:         params.Ref,
 								Breadcrumbs: breadcrumbs(params.Name, blob.Path, true),
+								RawHref:     rawHref,
 							},
 							CSS:      template.CSS(css.String()),
 							Blob:     blob,
styles_gen.go
@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"os"
 	"path/filepath"
-	"regexp"
 	"strings"
 
 	"github.com/alecthomas/chroma/v2"
@@ -82,24 +81,7 @@ func renderChromaCSS(theme string) (string, error) {
 	if err := formatter.WriteCSS(&buf, style); err != nil {
 		return "", err
 	}
-	return stripChromaBase(buf.String()), nil
-}
-
-// stripChromaBase removes background-color and color declarations from
-// top-level .chroma and .bg rules, so the code block background and default
-// text color are controlled by CSS variables from the layout theme instead.
-// Chroma outputs single-line rules like: .chroma { background-color: #fff; color: #000; }
-var reChromaBase = regexp.MustCompile(`(\.chroma|\.bg)\s*\{([^}]*)\}`)
-var reStripBGColor = regexp.MustCompile(`\s*background-color:\s*[^;]+;`)
-var reStripColor = regexp.MustCompile(`\s*color:\s*[^;]+;`)
-
-func stripChromaBase(css string) string {
-	return reChromaBase.ReplaceAllStringFunc(css, func(match string) string {
-		parts := reChromaBase.FindStringSubmatch(match)
-		body := reStripBGColor.ReplaceAllString(parts[2], "")
-		body = reStripColor.ReplaceAllString(body, "")
-		return parts[1] + " {" + body + " }"
-	})
+	return buf.String(), nil
 }
 
 func combineThemeCSS(light, dark string) string {
@@ -109,9 +91,7 @@ func combineThemeCSS(light, dark string) string {
 	if dark == "" || light == dark {
 		return light
 	}
-	return light +
-		"\n@media (prefers-color-scheme: dark) {\n" + dark + "\n}\n" +
-		"\n[data-theme=\"dark\"] {\n" + dark + "\n}\n"
+	return light + "\n@media (prefers-color-scheme: dark) {\n" + dark + "\n}\n"
 }
 
 func renderDiffCSS(theme string) (string, error) {