diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-09 20:44:58 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-09 20:44:58 +0300 |
| commit | 3e61d09873065f5342efc414ee3ea0d5fdc4c767 (patch) | |
| tree | 7d0ac51cfb41b4774db6292deeb0cc3dce93cf07 /cmd | |
| parent | 51f95f88ca78471a50b3fc62dbcea8edb609dc80 (diff) | |
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 <noreply@anthropic.com>
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/snonux/main.go | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/cmd/snonux/main.go b/cmd/snonux/main.go new file mode 100644 index 0000000..5a9c9d3 --- /dev/null +++ b/cmd/snonux/main.go @@ -0,0 +1,113 @@ +// Command snonux is the static microblog generator for snonux.foo. +// It processes new source files from the input directory into post directories, +// then regenerates all HTML pages and the Atom feed in the output directory. +// +// Usage: +// +// snonux --input ./inbox --output ./outdir [--base-url https://snonux.foo] +package main + +import ( + "flag" + "fmt" + "log" + "os" + "path/filepath" + + "codeberg.org/snonux/snonux/internal/config" + "codeberg.org/snonux/snonux/internal/generator" + "codeberg.org/snonux/snonux/internal/processor" +) + +func main() { + cfg, err := parseFlags() + if err != nil { + log.Fatalf("error: %v", err) + } + + if err := run(cfg); err != nil { + log.Fatalf("error: %v", err) + } +} + +// parseFlags reads CLI flags and returns a validated Config. +func parseFlags() (*config.Config, error) { + cfg := &config.Config{} + + flag.StringVar(&cfg.InputDir, "input", "./inbox", "directory containing new source files to process") + flag.StringVar(&cfg.OutputDir, "output", "~/git/snonux.foo/dist", "root directory for generated static site output") + flag.StringVar(&cfg.BaseURL, "base-url", "https://snonux.foo", "canonical base URL used in Atom feed links") + flag.StringVar(&cfg.Theme, "theme", "neon", "visual theme: aurora, brutalist, glass, matrix, minimal, neon, ocean, paper, retro, synthwave, terminal") + flag.Parse() + + var err error + + cfg.InputDir, err = expandHome(cfg.InputDir) + if err != nil { + return nil, fmt.Errorf("input dir: %w", err) + } + + cfg.OutputDir, err = expandHome(cfg.OutputDir) + if err != nil { + return nil, fmt.Errorf("output dir: %w", err) + } + + if err := ensureDir(cfg.InputDir); err != nil { + return nil, fmt.Errorf("input dir: %w", err) + } + + if err := ensureDir(cfg.OutputDir); err != nil { + return nil, fmt.Errorf("output dir: %w", err) + } + + return cfg, nil +} + +// expandHome replaces a leading ~ with the current user's home directory. +func expandHome(path string) (string, error) { + if len(path) == 0 || path[0] != '~' { + return path, nil + } + + home, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("resolve home dir: %w", err) + } + + return filepath.Join(home, path[1:]), nil +} + +// run executes both pipeline phases: process inputs, then regenerate pages. +func run(cfg *config.Config) error { + processed, err := processor.Run(cfg) + if err != nil { + return fmt.Errorf("processing input files: %w", err) + } + + log.Printf("processed %d new post(s) from %s", processed, cfg.InputDir) + + if err := generator.Run(cfg); err != nil { + return fmt.Errorf("generating site: %w", err) + } + + log.Printf("site regenerated in %s", cfg.OutputDir) + + return nil +} + +// ensureDir creates dir if it does not exist, or returns an error if path +// exists but is not a directory. +func ensureDir(dir string) error { + info, err := os.Stat(dir) + if os.IsNotExist(err) { + return os.MkdirAll(dir, 0o755) + } + if err != nil { + return err + } + if !info.IsDir() { + return fmt.Errorf("%s exists but is not a directory", dir) + } + + return nil +} |
