package webui import ( "bytes" "context" "fmt" "html" "io" "path/filepath" "strings" "github.com/a-h/templ" "github.com/niklasfasching/go-org/org" "github.com/pogo-vcs/pogo/filecontents" "github.com/yuin/goldmark" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/parser" gmhtml "github.com/yuin/goldmark/renderer/html" ) // RenderFile renders a file based on its extension func RenderFile(filename string, hash string) (string, error) { // Open the file using the filecontents abstraction with decompression f, err := filecontents.OpenFileByHash(hash) if err != nil { return "", fmt.Errorf("open %s hash %s: %w", filename, hash, err) } defer f.Close() // Read the file content content, err := io.ReadAll(f) if err != nil { return "", err } // Get file extension ext := strings.ToLower(filepath.Ext(filename)) switch ext { case ".md", ".markdown": return renderMarkdown(content) case ".org": orgStr, err := renderOrgMode(content) if err != nil { return "", err } return fmt.Sprintf("
" + html.EscapeString(string(content)) + "", nil
}
}
// RenderFileComponent returns a templ component for rendering file content
func RenderFileComponent(filename string, hash string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
rendered, err := RenderFile(filename, hash)
if err != nil {
return err
}
_, err = io.WriteString(w, rendered)
return err
})
}
func renderMarkdown(content []byte) (string, error) {
md := goldmark.New(
goldmark.WithExtensions(extension.GFM),
goldmark.WithParserOptions(
parser.WithAutoHeadingID(),
),
goldmark.WithRendererOptions(
gmhtml.WithHardWraps(),
),
)
var buf bytes.Buffer
if err := md.Convert(content, &buf); err != nil {
return "", err
}
return buf.String(), nil
}
var orgMode = org.New()
func renderOrgMode(content []byte) (string, error) {
doc := orgMode.Parse(bytes.NewReader(content), "")
if doc.Error != nil {
return "", doc.Error
}
writer := org.NewHTMLWriter()
str, err := doc.Write(writer)
if err != nil {
return "", err
}
return str, nil
}