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:
10
PLAN.md
10
PLAN.md
@@ -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.
|
||||
|
||||
28
content.go
28
content.go
@@ -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
42
themes/default/base.html
Normal 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>
|
||||
53
themes/default/baseof.html
Normal file
53
themes/default/baseof.html
Normal 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>
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user