From 3e61d09873065f5342efc414ee3ea0d5fdc4c767 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 9 Apr 2026 20:44:58 +0300 Subject: add snonux static microblog generator Full Go implementation with: - txt/md/image/audio input processing, URL auto-linking in .txt files - Paginated HTML output with Atom feed - 11 visual themes: neon, terminal, synthwave, minimal, brutalist, paper, aurora, matrix, ocean, retro, glass (selectable via --theme flag) - Keyboard navigation (j/k/arrows, Enter modal, h/l page nav) - Shared nav templates (navhints, navmodal, navscript) across all themes - Magefile build automation; integration test suite covering all themes Co-Authored-By: Claude Sonnet 4.6 --- internal/processor/markdown.go | 68 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 internal/processor/markdown.go (limited to 'internal/processor/markdown.go') diff --git a/internal/processor/markdown.go b/internal/processor/markdown.go new file mode 100644 index 0000000..8d69bfe --- /dev/null +++ b/internal/processor/markdown.go @@ -0,0 +1,68 @@ +package processor + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/yuin/goldmark" + "github.com/yuin/goldmark/extension" + "github.com/yuin/goldmark/renderer/html" +) + +// imageRefPattern matches Markdown image syntax: ![alt](filename) +// We use it to discover local asset references that must be copied. +var imageRefPattern = regexp.MustCompile(`!\[[^\]]*\]\(([^)]+)\)`) + +// processMd converts a Markdown file to an HTML snippet. +// Returns the HTML and a list of local image filenames referenced in the document. +// Referenced images that exist alongside the source file are returned so the +// caller can copy them into the post asset directory. +func processMd(path string) (htmlContent string, localImages []string, err error) { + data, err := os.ReadFile(path) + if err != nil { + return "", nil, fmt.Errorf("read markdown %s: %w", path, err) + } + + // Collect local image references so the caller can copy them as assets. + localImages = findLocalImages(string(data), filepath.Dir(path)) + + md := goldmark.New( + goldmark.WithExtensions(extension.GFM), + goldmark.WithRendererOptions( + html.WithUnsafe(), // Allow raw HTML in markdown (user-controlled content). + ), + ) + + var buf bytes.Buffer + if err := md.Convert(data, &buf); err != nil { + return "", nil, fmt.Errorf("convert markdown %s: %w", path, err) + } + + return buf.String(), localImages, nil +} + +// findLocalImages returns image filenames referenced in markdown that actually +// exist in sourceDir. Remote URLs (http/https) are ignored. +func findLocalImages(mdContent, sourceDir string) []string { + matches := imageRefPattern.FindAllStringSubmatch(mdContent, -1) + var locals []string + + for _, m := range matches { + ref := m[1] + // Skip remote URLs. + if strings.HasPrefix(ref, "http://") || strings.HasPrefix(ref, "https://") { + continue + } + + candidate := filepath.Join(sourceDir, ref) + if _, err := os.Stat(candidate); err == nil { + locals = append(locals, filepath.Base(ref)) + } + } + + return locals +} -- cgit v1.2.3