talas-group/talas-wiki/internal/wiki/exporter.go
senke 66471934af Initial commit: Talas Group project management & documentation
Knowledge base of ~80+ markdown files across 14 domains (00-13),
Logseq graph, hardware design files (KiCAD), infrastructure configs,
and talas-wiki static site.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 20:10:41 +02:00

111 lines
3.2 KiB
Go

package wiki
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
type Exporter struct {
idx *Index
renderer *Renderer
}
func NewExporter(idx *Index, renderer *Renderer) *Exporter {
return &Exporter{idx: idx, renderer: renderer}
}
func (e *Exporter) ExportTarGz(w io.Writer) error {
gzw := gzip.NewWriter(w)
defer gzw.Close()
tw := tar.NewWriter(gzw)
defer tw.Close()
css := exportCSS()
// Export all pages
pages := e.idx.GetAllPages()
for _, page := range pages {
html, err := e.renderer.RenderPage(page)
if err != nil {
continue
}
full := wrapExportHTML(page.Title, html, css)
data := []byte(full)
tw.WriteHeader(&tar.Header{
Name: page.URLPath + ".html",
Size: int64(len(data)),
Mode: 0644,
})
tw.Write(data)
}
// Export index
indexHTML := e.buildIndex(pages, css)
indexData := []byte(indexHTML)
tw.WriteHeader(&tar.Header{Name: "index.html", Size: int64(len(indexData)), Mode: 0644})
tw.Write(indexData)
// Export assets (non-md files)
filepath.Walk(e.idx.GetDocsRoot(), func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
return nil
}
relPath, _ := filepath.Rel(e.idx.GetDocsRoot(), path)
if strings.HasSuffix(relPath, ".md") || e.idx.isExcluded(relPath) {
return nil
}
ext := strings.ToLower(filepath.Ext(relPath))
if ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif" || ext == ".svg" || ext == ".pdf" {
data, err := os.ReadFile(path)
if err != nil {
return nil
}
tw.WriteHeader(&tar.Header{Name: "assets/" + relPath, Size: int64(len(data)), Mode: 0644})
tw.Write(data)
}
return nil
})
return nil
}
func (e *Exporter) buildIndex(pages []*Page, css string) string {
var b strings.Builder
b.WriteString("<h1>Talas Wiki — Export</h1>\n")
domains := e.idx.GetDomains()
for _, dom := range domains {
b.WriteString(fmt.Sprintf("<h2>%s %s</h2>\n<ul>\n", dom.Number, dom.Name))
for _, page := range pages {
if page.Domain == dom.FullDir {
b.WriteString(fmt.Sprintf(`<li><a href="%s.html">%s</a></li>`+"\n", page.URLPath, page.Title))
}
}
b.WriteString("</ul>\n")
}
return wrapExportHTML("Talas Wiki — Export", b.String(), css)
}
func wrapExportHTML(title, body, css string) string {
return fmt.Sprintf(`<!DOCTYPE html>
<html lang="fr">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>%s</title><style>%s</style></head>
<body><div class="export-page">%s</div></body></html>`, title, css, body)
}
func exportCSS() string {
return `body{background:#121212;color:#a3be8c;font-family:'JetBrains Mono',Consolas,monospace;line-height:1.7;font-size:14px;margin:0}
.export-page{max-width:800px;margin:auto;padding:32px;border-left:2px solid #a3be8c}
h1,h2,h3{color:#eceff4} a{color:#88c0d0;text-decoration:none;border-bottom:1px dashed rgba(136,192,208,.3)}
a:hover{background:#a3be8c;color:#121212} pre{background:#0e0e0e;border:1px solid #2e3440;padding:16px;overflow-x:auto}
code{color:#88c0d0} table{border-collapse:collapse;width:100%} th,td{border:1px solid #2e3440;padding:8px}
th{background:#0e0e0e;color:#88c0d0} blockquote{border-left:3px solid #ebcb8b;padding:8px 16px;color:#6b7f5a}
img{max-width:100%}`
}