Commit f52df1c
Changed files (17)
pkg/templates/blob.gohtml
@@ -1,8 +1,11 @@
{{- /*gotype: github.com/antonmedv/gitmal/pkg/templates.BlobParams */ -}}
{{ define "head" }}
+ {{ if .InlineStyles }}
+ <style>{{ .CSS }}
+ {{ else }}
+ <link rel="stylesheet" href="{{ .RootHref }}chroma.css">
<style>
- {{ .CSS }}
-
+ {{ end }}
[id] {
scroll-margin-top: var(--header-height);
}
pkg/templates/commit.gohtml
@@ -1,5 +1,6 @@
{{- /*gotype: github.com/antonmedv/gitmal/pkg/templates.CommitParams*/ -}}
{{ define "head" }}
+ {{ if not .InlineStyles }}<link rel="stylesheet" href="{{ .RootHref }}diff.css">{{ end }}
<style>
h1 code {
border-radius: var(--border-radius);
@@ -215,7 +216,7 @@
border-bottom-right-radius: 6px;
}
- {{ .DiffCSS }}
+ {{ if .InlineStyles }}{{ .DiffCSS }}{{ end }}
</style>
{{ end }}
pkg/templates/layout.gohtml
@@ -1,271 +1,15 @@
{{- /*gotype: github.com/antonmedv/gitmal/pkg/templates.LayoutParams*/ -}}
<!DOCTYPE html>
-<html lang="en">
+<html lang="en"{{ if not .InlineStyles }}{{ if .Dark }} data-theme="dark"{{ end }}{{ end }}>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ .Title }}</title>
- <style>
- {{ if .Dark }}
- :root {
- --c-indigo-1: #a8b1ff;
- --c-indigo-2: #5c73e7;
- --c-indigo-3: #3e63dd;
- --c-green: #57ab5a;
- --c-red: #e5534b;
- --c-yellow: #c69026;
- --c-dir: #9198a1;
- --c-gray-soft: rgba(101, 117, 133, .16);
- --c-bg: #1b1b1f;
- --c-bg-alt: #161618;
- --c-bg-elv: #202127;
- --c-text-1: rgba(255, 255, 245, .86);
- --c-text-2: rgba(235, 235, 245, .6);
- --c-text-3: rgba(235, 235, 245, .38);
- --c-border: #3c3f44;
- --c-divider: #2e2e32;
- }
-
- {{ else }}
- :root {
- --c-indigo-1: #3451b2;
- --c-indigo-2: #3a5ccc;
- --c-indigo-3: #5672cd;
- --c-green: #1a7f37;
- --c-red: #c53030;
- --c-yellow: #9a6700;
- --c-dir: #54aeff;
- --c-gray-soft: rgba(142, 150, 170, .14);
- --c-bg: #ffffff;
- --c-bg-alt: #f6f6f7;
- --c-bg-elv: #ffffff;
- --c-text-1: rgba(60, 60, 67);
- --c-text-2: rgba(60, 60, 67, .78);
- --c-text-3: rgba(60, 60, 67, .56);
- --c-border: #c2c2c4;
- --c-divider: #e2e2e3;
- }
-
- {{ end }}
-
- :root {
- --c-brand-1: var(--c-indigo-1);
- --c-brand-2: var(--c-indigo-2);
- --c-brand-3: var(--c-indigo-3);
- --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
- --font-family-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
- --code-line-height: 20px;
- --code-font-size: 12px;
- --code-color: var(--c-brand-1);
- --code-bg: var(--c-gray-soft);
- --code-block-bg: var(--c-bg-alt);
- --code-block-color: var(--c-text-1);
- --header-height: 46px;
- --border-radius: 6px;
- --max-content-width: 1470px;
- }
-
- * {
- box-sizing: border-box;
- }
-
- body {
- margin: 0;
- padding: 0;
- font-family: var(--font-family), sans-serif;
- font-size: 14px;
- line-height: 1;
- color: var(--c-text-1);
- background-color: var(--c-bg);
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- -moz-text-size-adjust: none;
- -webkit-text-size-adjust: none;
- text-size-adjust: none;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- }
-
- .nowrap {
- white-space: nowrap;
- }
-
- h1 {
- margin-inline: 0;
- margin-block: 16px;
- font-size: 20px;
- font-weight: 600;
- }
-
- a {
- color: var(--c-brand-1);
- font-weight: 500;
- text-decoration: underline;
- text-underline-offset: 2px;
- text-decoration: inherit;
- touch-action: manipulation;
- }
-
- a:hover {
- color: var(--c-brand-2);
- text-decoration: underline;
- }
-
- .menu {
- background-color: var(--c-bg-alt);
- border-bottom: 1px solid var(--c-divider);
- overflow-x: auto;
- }
-
- .menu-content {
- display: flex;
- flex-direction: row;
- align-items: center;
- gap: 16px;
- padding-inline: 16px;
- max-width: var(--max-content-width);
- margin-inline: auto;
- }
-
- .menu-item {
- display: flex;
- align-items: center;
- border-bottom: 2px solid transparent;
- height: 56px;
- padding-inline: 8px;
- }
-
- .menu-item a {
- display: flex;
- flex-direction: row;
- gap: 8px;
- align-items: center;
- color: var(--c-text-1);
- padding: 8px 10px;
- border-radius: 4px;
- }
-
- .menu-item a:hover {
- background-color: var(--c-bg-elv);
- text-decoration: none;
- }
-
- .menu-item.selected {
- border-bottom-color: var(--c-brand-1);
- }
-
- .project-name {
- font-weight: 600;
- font-size: 16px;
- margin-inline: 16px;
- color: var(--c-text-1);
- text-decoration: none;
- }
-
- main {
- flex-grow: 1;
- width: 100%;
- max-width: var(--max-content-width);
- margin: 16px auto;
- }
-
- .main-content {
- padding-inline: 16px;
- }
-
- footer {
- padding: 12px 16px;
- background-color: var(--c-bg-alt);
- border-top: 1px solid var(--c-divider);
- color: var(--c-text-3);
- font-size: 12px;
- text-align: center;
- }
-
- .header-container {
- container-type: scroll-state;
- position: sticky;
- top: 0;
- }
-
- .header-container {
- @container scroll-state(stuck: top) {
- header {
- border-top: none;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- }
-
- .goto-top {
- display: flex;
- }
- }
- }
-
- header {
- display: flex;
- flex-direction: row;
- align-items: center;
- min-height: var(--header-height);
- padding-inline: 16px;
- background: var(--c-bg-alt);
- border: 1px solid var(--c-border);
- border-top-left-radius: var(--border-radius);
- border-top-right-radius: var(--border-radius);
- }
-
- header h1 {
- word-break: break-all;
- font-weight: 600;
- font-size: 16px;
- margin: 0;
- padding: 0;
- }
-
- .header-ref {
- color: var(--c-text-2);
- border: 1px solid var(--c-border);
- border-radius: 6px;
- padding: 6px 10px;
- margin-right: 10px;
- margin-left: -6px;
- }
-
- header .path {
- font-size: 16px;
- }
-
- .breadcrumbs {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- gap: 6px;
- font-size: 16px;
- }
-
- .breadcrumbs a {
- word-break: break-all;
- }
-
- .goto-top {
- display: none;
- margin-left: auto;
- padding: 6px 10px;
- background: none;
- border: none;
- border-radius: 6px;
- gap: 4px;
- align-items: center;
- color: var(--c-text-1);
- cursor: pointer;
- }
-
- .goto-top:hover {
- background: var(--c-bg-elv);
- }
- </style>
+ {{ if .InlineStyles }}
+ <style>{{ LayoutCSSInline .Dark }}</style>
+ {{ else }}
+ <link rel="stylesheet" href="{{ .RootHref }}style.css">
+ {{ end }}
{{ template "head" . }}
</head>
<body>
pkg/templates/list.gohtml
@@ -1,5 +1,6 @@
{{- /*gotype: github.com/antonmedv/gitmal/pkg/templates.ListParams*/ -}}
{{ define "head" }}
+ {{ if and .Readme (not .InlineStyles) }}<link rel="stylesheet" href="{{ .RootHref }}markdown.css">{{ end }}
<style>
.files {
border: 1px solid var(--c-border);
@@ -83,7 +84,7 @@
}
}
- {{.CSSMarkdown}}
+ {{ if $.InlineStyles }}{{$.CSSMarkdown}}{{ end }}
{{ end }}
</style>
{{ end }}
pkg/templates/markdown.gohtml
@@ -1,5 +1,12 @@
{{- /*gotype: github.com/antonmedv/gitmal/pkg/templates.MarkdownParams*/ -}}
{{ define "head" }}
+ {{ if .InlineStyles }}
+ <style>
+ {{.CSSMarkdown}}
+ </style>
+ {{ else }}
+ <link rel="stylesheet" href="{{ .RootHref }}markdown.css">
+ {{ end }}
<style>
[id] {
scroll-margin-top: var(--header-height);
@@ -29,8 +36,6 @@
padding: 16px;
}
}
-
- {{.CSSMarkdown}}
</style>
{{ end }}
pkg/templates/styles.go
@@ -0,0 +1,317 @@
+package templates
+
+import . "html/template"
+
+// Layout CSS constants โ these must be kept in sync with the inline CSS
+// block in layout.gohtml (used when --inline-styles is set). Both files
+// define the same rules; styles.go adds a [data-theme="dark"] variant
+// for the external style.css file.
+
+const LayoutCSSVarLight = `:root {
+ --c-indigo-1: #3451b2;
+ --c-indigo-2: #3a5ccc;
+ --c-indigo-3: #5672cd;
+ --c-green: #1a7f37;
+ --c-red: #c53030;
+ --c-yellow: #9a6700;
+ --c-dir: #54aeff;
+ --c-gray-soft: rgba(142, 150, 170, .14);
+ --c-bg: #ffffff;
+ --c-bg-alt: #f6f6f7;
+ --c-bg-elv: #ffffff;
+ --c-text-1: rgba(60, 60, 67);
+ --c-text-2: rgba(60, 60, 67, .78);
+ --c-text-3: rgba(60, 60, 67, .56);
+ --c-border: #c2c2c4;
+ --c-divider: #e2e2e3;
+}
+`
+
+const LayoutCSSVarDarkInline = `:root {
+ --c-indigo-1: #a8b1ff;
+ --c-indigo-2: #5c73e7;
+ --c-indigo-3: #3e63dd;
+ --c-green: #57ab5a;
+ --c-red: #e5534b;
+ --c-yellow: #c69026;
+ --c-dir: #9198a1;
+ --c-gray-soft: rgba(101, 117, 133, .16);
+ --c-bg: #1b1b1f;
+ --c-bg-alt: #161618;
+ --c-bg-elv: #202127;
+ --c-text-1: rgba(255, 255, 245, .86);
+ --c-text-2: rgba(235, 235, 245, .6);
+ --c-text-3: rgba(235, 235, 245, .38);
+ --c-border: #3c3f44;
+ --c-divider: #2e2e32;
+}
+`
+
+const LayoutCSSVarDarkCombo = `[data-theme="dark"] {
+ --c-indigo-1: #a8b1ff;
+ --c-indigo-2: #5c73e7;
+ --c-indigo-3: #3e63dd;
+ --c-green: #57ab5a;
+ --c-red: #e5534b;
+ --c-yellow: #c69026;
+ --c-dir: #9198a1;
+ --c-gray-soft: rgba(101, 117, 133, .16);
+ --c-bg: #1b1b1f;
+ --c-bg-alt: #161618;
+ --c-bg-elv: #202127;
+ --c-text-1: rgba(255, 255, 245, .86);
+ --c-text-2: rgba(235, 235, 245, .6);
+ --c-text-3: rgba(235, 235, 245, .38);
+ --c-border: #3c3f44;
+ --c-divider: #2e2e32;
+}
+`
+
+const LayoutCSSShared = `
+:root {
+ --c-brand-1: var(--c-indigo-1);
+ --c-brand-2: var(--c-indigo-2);
+ --c-brand-3: var(--c-indigo-3);
+ --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ --font-family-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ --code-line-height: 20px;
+ --code-font-size: 12px;
+ --code-color: var(--c-brand-1);
+ --code-bg: var(--c-gray-soft);
+ --code-block-bg: var(--c-bg-alt);
+ --code-block-color: var(--c-text-1);
+ --header-height: 46px;
+ --border-radius: 6px;
+ --max-content-width: 1470px;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ font-family: var(--font-family), sans-serif;
+ font-size: 14px;
+ line-height: 1;
+ color: var(--c-text-1);
+ background-color: var(--c-bg);
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -moz-text-size-adjust: none;
+ -webkit-text-size-adjust: none;
+ text-size-adjust: none;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+.nowrap {
+ white-space: nowrap;
+}
+
+h1 {
+ margin-inline: 0;
+ margin-block: 16px;
+ font-size: 20px;
+ font-weight: 600;
+}
+
+a {
+ color: var(--c-brand-1);
+ font-weight: 500;
+ text-decoration: underline;
+ text-underline-offset: 2px;
+ text-decoration: inherit;
+ touch-action: manipulation;
+}
+
+a:hover {
+ color: var(--c-brand-2);
+ text-decoration: underline;
+}
+
+.menu {
+ background-color: var(--c-bg-alt);
+ border-bottom: 1px solid var(--c-divider);
+ overflow-x: auto;
+}
+
+.menu-content {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 16px;
+ padding-inline: 16px;
+ max-width: var(--max-content-width);
+ margin-inline: auto;
+}
+
+.menu-item {
+ display: flex;
+ align-items: center;
+ border-bottom: 2px solid transparent;
+ height: 56px;
+ padding-inline: 8px;
+}
+
+.menu-item a {
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ align-items: center;
+ color: var(--c-text-1);
+ padding: 8px 10px;
+ border-radius: 4px;
+}
+
+.menu-item a:hover {
+ background-color: var(--c-bg-elv);
+ text-decoration: none;
+}
+
+.menu-item.selected {
+ border-bottom-color: var(--c-brand-1);
+}
+
+.project-name {
+ font-weight: 600;
+ font-size: 16px;
+ margin-inline: 16px;
+ color: var(--c-text-1);
+ text-decoration: none;
+}
+
+main {
+ flex-grow: 1;
+ width: 100%;
+ max-width: var(--max-content-width);
+ margin: 16px auto;
+}
+
+.main-content {
+ padding-inline: 16px;
+}
+
+footer {
+ padding: 12px 16px;
+ background-color: var(--c-bg-alt);
+ border-top: 1px solid var(--c-divider);
+ color: var(--c-text-3);
+ font-size: 12px;
+ text-align: center;
+}
+
+.header-container {
+ container-type: scroll-state;
+ position: sticky;
+ top: 0;
+}
+
+@container scroll-state(stuck: top) {
+ header {
+ border-top: none;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ }
+
+ .goto-top {
+ display: flex;
+ }
+}
+
+header {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ min-height: var(--header-height);
+ padding-inline: 16px;
+ background: var(--c-bg-alt);
+ border: 1px solid var(--c-border);
+ border-top-left-radius: var(--border-radius);
+ border-top-right-radius: var(--border-radius);
+}
+
+header h1 {
+ word-break: break-all;
+ font-weight: 600;
+ font-size: 16px;
+ margin: 0;
+ padding: 0;
+}
+
+.header-ref {
+ color: var(--c-text-2);
+ border: 1px solid var(--c-border);
+ border-radius: 6px;
+ padding: 6px 10px;
+ margin-right: 10px;
+ margin-left: -6px;
+}
+
+header .path {
+ font-size: 16px;
+}
+
+.breadcrumbs {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ gap: 6px;
+ font-size: 16px;
+}
+
+.breadcrumbs a {
+ word-break: break-all;
+}
+
+.raw-button {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ padding: 4px 12px;
+ margin-left: auto;
+ font-size: 13px;
+ font-weight: 500;
+ line-height: 20px;
+ color: var(--c-text-1);
+ background: var(--c-bg);
+ border: 1px solid var(--c-border);
+ border-radius: 6px;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.raw-button:hover {
+ background: var(--c-bg-elv);
+}
+
+.goto-top {
+ display: none;
+ padding: 6px 10px;
+ background: none;
+ border: none;
+ border-radius: 6px;
+ gap: 4px;
+ align-items: center;
+ color: var(--c-text-1);
+ cursor: pointer;
+}
+
+.goto-top:hover {
+ background: var(--c-bg-elv);
+}
+`
+
+func LayoutCSSCombo() string {
+ return LayoutCSSVarLight + LayoutCSSVarDarkCombo + LayoutCSSShared
+}
+
+func LayoutCSSInline(dark bool) CSS {
+ if dark {
+ return CSS(LayoutCSSVarDarkInline + LayoutCSSShared)
+ }
+ return CSS(LayoutCSSVarLight + LayoutCSSShared)
+}
pkg/templates/templates.go
@@ -21,6 +21,7 @@ var funcs = FuncMap{
"FileTreeParams": func(node []*FileTree) FileTreeParams {
return FileTreeParams{Nodes: node}
},
+ "LayoutCSSInline": LayoutCSSInline,
}
//go:embed css/markdown_light.css
@@ -73,6 +74,7 @@ type LayoutParams struct {
RootHref string
CurrentRefDir string
Selected string
+ InlineStyles bool
}
type HeaderParams struct {
blob.go
@@ -141,6 +141,7 @@ func generateBlobs(files []git.Blob, params Params) error {
RootHref: rootHref,
CurrentRefDir: params.Ref.DirName(),
Selected: "code",
+ InlineStyles: params.InlineStyles,
},
HeaderParams: templates.HeaderParams{
Ref: params.Ref,
@@ -199,6 +200,7 @@ func generateBlobs(files []git.Blob, params Params) error {
RootHref: rootHref,
CurrentRefDir: params.Ref.DirName(),
Selected: "code",
+ InlineStyles: params.InlineStyles,
},
HeaderParams: templates.HeaderParams{
Ref: params.Ref,
branches.go
@@ -54,6 +54,7 @@ func generateBranches(branches []git.Ref, defaultBranch string, params Params) e
RootHref: rootHref,
CurrentRefDir: params.DefaultRef.DirName(),
Selected: "branches",
+ InlineStyles: params.InlineStyles,
},
Branches: entries,
})
commit.go
@@ -242,6 +242,7 @@ func generateCommitPage(commit git.Commit, params Params) error {
RootHref: rootHref,
CurrentRefDir: currentRef.DirName(),
Selected: "commits",
+ InlineStyles: params.InlineStyles,
},
Commit: commit,
DiffCSS: template.CSS(cssBuf.String()),
commits_list.go
@@ -68,6 +68,7 @@ func generateLogForBranch(allCommits []git.Commit, params Params) error {
RootHref: rootHref,
CurrentRefDir: params.Ref.DirName(),
Selected: "commits",
+ InlineStyles: params.InlineStyles,
},
HeaderParams: templates.HeaderParams{
Header: "Commits",
index.go
@@ -107,6 +107,7 @@ func generateIndex(files []git.Blob, params Params) error {
RootHref: rootHref,
CurrentRefDir: params.Ref.DirName(),
Selected: "code",
+ InlineStyles: params.InlineStyles,
},
HeaderParams: templates.HeaderParams{
Ref: params.Ref,
list.go
@@ -190,6 +190,7 @@ func generateLists(files []git.Blob, params Params) error {
RootHref: rootHref,
CurrentRefDir: params.Ref.DirName(),
Selected: "code",
+ InlineStyles: params.InlineStyles,
},
HeaderParams: templates.HeaderParams{
Ref: params.Ref,
main.go
@@ -24,17 +24,19 @@ var (
flagMinify bool
flagGzip bool
flagGit bool
+ flagInlineStyles bool
)
type Params struct {
- Owner string
- Name string
- RepoDir string
- Ref git.Ref
- OutputDir string
- Style string
- Dark bool
- DefaultRef git.Ref
+ Owner string
+ Name string
+ RepoDir string
+ Ref git.Ref
+ OutputDir string
+ Style string
+ Dark bool
+ DefaultRef git.Ref
+ InlineStyles bool
}
func main() {
@@ -70,6 +72,7 @@ func main() {
flag.BoolVar(&flagMinify, "minify", false, "Minify all generated HTML files")
flag.BoolVar(&flagGzip, "gzip", false, "Compress all generated HTML files")
flag.BoolVar(&flagGit, "git", false, "Generate static files for Git dumb HTTP protocol")
+ flag.BoolVar(&flagInlineStyles, "inline-styles", false, "Keep all CSS inline in HTML instead of external files")
flag.Usage = usage
flag.Parse()
@@ -148,13 +151,20 @@ func main() {
// Start generating pages
params := Params{
- Owner: flagOwner,
- Name: flagName,
- RepoDir: input,
- OutputDir: outputDir,
- Style: flagTheme,
- Dark: themeColor == "dark",
- DefaultRef: git.NewRef(flagDefaultBranch),
+ Owner: flagOwner,
+ Name: flagName,
+ RepoDir: input,
+ OutputDir: outputDir,
+ Style: flagTheme,
+ Dark: themeColor == "dark",
+ DefaultRef: git.NewRef(flagDefaultBranch),
+ InlineStyles: flagInlineStyles,
+ }
+
+ if !params.InlineStyles {
+ if err := generateCSSFiles(params); err != nil {
+ panic(err)
+ }
}
commits := make(map[string]git.Commit)
post_process.go
@@ -20,7 +20,7 @@ import (
)
func postProcessHTML(root string, doMinify bool, doGzip bool) error {
- // 1) Collect all HTML files first
+ // 1) Collect all HTML and CSS files
var files []string
if err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
@@ -31,6 +31,8 @@ func postProcessHTML(root string, doMinify bool, doGzip bool) error {
}
if strings.HasSuffix(d.Name(), ".html") {
files = append(files, path)
+ } else if doMinify && strings.HasSuffix(d.Name(), ".css") {
+ files = append(files, path)
}
return nil
}); err != nil {
@@ -74,14 +76,23 @@ func postProcessHTML(root string, doMinify bool, doGzip bool) error {
for path := range jobs {
data, err := os.ReadFile(path)
if err == nil && doMinify {
- if md, e := m.Bytes("text/html", data); e == nil {
+ mime := "text/html"
+ if strings.HasSuffix(path, ".css") {
+ mime = "text/css"
+ }
+ if md, e := m.Bytes(mime, data); e == nil {
data = md
} else {
err = e
}
}
if err == nil {
- if doGzip {
+ if strings.HasSuffix(path, ".css") {
+ // CSS files: only minify, no gzip
+ if e := os.WriteFile(path, data, 0o644); e != nil {
+ err = e
+ }
+ } else if doGzip {
// write to file.html.gz
gzPath := path + ".gz"
if e := writeGzip(gzPath, data); e != nil {
styles_gen.go
@@ -0,0 +1,70 @@
+package main
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/alecthomas/chroma/v2"
+ "github.com/alecthomas/chroma/v2/formatters/html"
+ "github.com/alecthomas/chroma/v2/styles"
+
+ "github.com/antonmedv/gitmal/pkg/templates"
+)
+
+func generateCSSFiles(params Params) error {
+ out := params.OutputDir
+
+ if err := os.MkdirAll(out, 0o755); err != nil {
+ return err
+ }
+
+ if err := os.WriteFile(filepath.Join(out, "style.css"), []byte(templates.LayoutCSSCombo()), 0o644); err != nil {
+ return err
+ }
+
+ // markdown.css for the active theme
+ var mdCSS string
+ if params.Dark {
+ mdCSS = templates.CSSMarkdownDark
+ } else {
+ mdCSS = templates.CSSMarkdownLight
+ }
+ if err := os.WriteFile(filepath.Join(out, "markdown.css"), []byte(mdCSS), 0o644); err != nil {
+ return err
+ }
+
+ // chroma.css for syntax highlighting and diff.css for diff highlighting
+ style := styles.Get(params.Style)
+ if style != nil {
+ formatter := html.New(
+ html.WithClasses(true),
+ html.WithCSSComments(false),
+ )
+ var cssBuf strings.Builder
+ if err := formatter.WriteCSS(&cssBuf, style); err != nil {
+ return err
+ }
+ if err := os.WriteFile(filepath.Join(out, "chroma.css"), []byte(cssBuf.String()), 0o644); err != nil {
+ return err
+ }
+
+ diffFormatter := html.New(
+ html.WithClasses(true),
+ html.WithCSSComments(false),
+ html.WithCustomCSS(map[chroma.TokenType]string{
+ chroma.GenericInserted: "display: block;",
+ chroma.GenericDeleted: "display: block;",
+ }),
+ )
+ var diffBuf strings.Builder
+ if err := diffFormatter.WriteCSS(&diffBuf, style); err != nil {
+ return err
+ }
+ if err := os.WriteFile(filepath.Join(out, "diff.css"), []byte(diffBuf.String()), 0o644); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}