feat: integrate HTML template engine with theme selection

- Add theme configuration to WebServer struct
- Create default theme with base.html layout template
- Update content.go to apply theme templates to generated HTML
- Output is now full HTML documents with styling

Co-Authored-By: Claude (glm-5) <noreply@anthropic.com>
This commit is contained in:
2026-03-05 02:17:12 +01:00
parent a1d054dfe4
commit 1d47c5a930
5 changed files with 131 additions and 5 deletions

10
PLAN.md
View File

@@ -10,7 +10,7 @@
## Current Status
- [x] Project backbone exists (HTTP server, graceful shutdown, config reading).
- [x] Markdown parsing logic implemented (using goldmark).
- [ ] HTML Template engine integrated (Hugo-like theme selection).
- [x] HTML Template engine integrated (Hugo-like theme selection).
- [ ] PDF Generation implemented (Pure Go library selected and integrated).
- [x] CLI Mode (`gocv`) generates static files to `./output` and exits.
- [ ] Serve Mode (`gocv serve`) hosts HTML and serves PDF on demand.
@@ -19,11 +19,12 @@
## Active Task
- [x] Analyze existing backbone code and integrate Markdown parsing.
- [ ] Integrate HTML template engine with theme selection.
- [x] Integrate HTML template engine with theme selection.
- [ ] Implement Serve Mode with file watching for live reload.
## Known Issues / Blockers
- [ ] Identify best Pure Go PDF library that supports HTML/CSS (or define CSS subset).
- [ ] Current output is raw HTML fragments without full HTML document structure.
- [x] Current output is raw HTML fragments without full HTML document structure.
## Completed Log
- [x] Initial project structure defined.
@@ -31,3 +32,6 @@
- [x] CLI mode vs Serve mode distinction implemented (checks os.Args[1] for "serve").
- [x] Content reading from `./content` directory implemented.
- [x] Markdown to HTML conversion using goldmark library.
- [x] Theme system with templates/themes/{theme}/base.html structure.
- [x] Config.yaml theme setting (defaults to "default").
- [x] Output now generates complete HTML documents with styling.

View File

@@ -3,6 +3,7 @@ package main
import (
"bytes"
"fmt"
"html/template"
"os"
"path/filepath"
"strings"
@@ -18,6 +19,12 @@ type ContentFile struct {
HTML string // Converted HTML
}
// PageData represents data passed to the template
type PageData struct {
Title string
Content template.HTML
}
// generateOutput reads content from ./content, processes it, and writes to ./output
func generateOutput() error {
// Ensure content directory exists
@@ -30,6 +37,13 @@ func generateOutput() error {
return fmt.Errorf("failed to create output directory: %w", err)
}
// Load the theme template
themePath := filepath.Join("themes", ws.Theme, "base.html")
tmpl, err := template.ParseFiles(themePath)
if err != nil {
return fmt.Errorf("failed to load theme template %s: %w", themePath, err)
}
// Read all markdown files from content directory
files, err := readContentFiles()
if err != nil {
@@ -52,12 +66,22 @@ func generateOutput() error {
}
file.HTML = html
// Apply theme template
var output bytes.Buffer
data := PageData{
Title: file.Name + " - " + ws.AppName,
Content: template.HTML(file.HTML),
}
if err := tmpl.Execute(&output, data); err != nil {
return fmt.Errorf("failed to execute template for %s: %w", file.SourcePath, err)
}
// Write output
outputFile := filepath.Join(outputPath, file.Name+".html")
if err := os.WriteFile(outputFile, []byte(html), 0644); err != nil {
if err := os.WriteFile(outputFile, output.Bytes(), 0644); err != nil {
return fmt.Errorf("failed to write %s: %w", outputFile, err)
}
fmt.Printf(" -> Written: %s\n", outputPath)
fmt.Printf(" -> Written: %s\n", outputFile)
}
return nil

42
themes/default/base.html Normal file
View File

@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Title}}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
color: #333;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 1.5em;
margin-bottom: 0.5em;
}
code {
background: #f4f4f4;
padding: 0.2em 0.4em;
border-radius: 3px;
}
pre {
background: #f4f4f4;
padding: 1em;
border-radius: 5px;
overflow-x: auto;
}
pre code {
padding: 0;
}
a {
color: #0066cc;
}
</style>
</head>
<body>
{{.Content}}
</body>
</html>

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ .Title }}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
color: #333;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 1.5em;
margin-bottom: 0.5em;
}
code {
background: #f4f4f4;
padding: 0.2em 0.4em;
border-radius: 3px;
}
pre {
background: #f4f4f4;
padding: 1em;
border-radius: 5px;
overflow-x: auto;
}
pre code {
padding: 0;
}
blockquote {
border-left: 4px solid #ddd;
margin: 0;
padding-left: 1em;
color: #666;
}
a {
color: #0066cc;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<main>
{{ .Content }}
</main>
</body>
</html>

View File

@@ -12,6 +12,7 @@ import (
type WebServer struct {
HTTPServer *http.Server
AppName string `yaml:"app_name"`
Theme string `yaml:"theme"`
Listen WSListen `yaml:"listen"`
}
@@ -27,6 +28,8 @@ func (s *WebServer) Initialize() {
Port: "80",
}
s.AppName = "Go Template Container Web Server"
s.Theme = "default"
s.Theme = "default"
// Attempt to read the config file (try both config.yml and config.yaml)
var configFile []byte