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>
111 lines
3.2 KiB
Go
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%}`
|
|
}
|