diff --git a/go.mod b/go.mod index 5ea7465..53e5430 100644 --- a/go.mod +++ b/go.mod @@ -7,4 +7,8 @@ require ( gopkg.in/yaml.v2 v2.4.0 ) -require github.com/yuin/goldmark v1.7.16 // indirect +require ( + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/yuin/goldmark v1.7.16 // indirect + golang.org/x/sys v0.13.0 // indirect +) diff --git a/go.sum b/go.sum index ed41eb4..c1503a3 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,11 @@ +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE= github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/main.go b/main.go index 795a5d5..03fa396 100644 --- a/main.go +++ b/main.go @@ -52,6 +52,21 @@ func runCLIMode() { func runServeMode() { fmt.Println("Running in Serve mode...") + // Generate initial output + fmt.Println("Generating initial output...") + if err := generateOutput(); err != nil { + fmt.Printf("Error generating initial output: %s\n", err) + } + + // Start file watcher for live reload + watcher, err := startWatcher() + if err != nil { + fmt.Printf("Warning: Failed to start file watcher: %s\n", err) + } + if watcher != nil { + defer watcher.Close() + } + // Create a channel to receive the OS signals sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM) @@ -68,7 +83,7 @@ func runServeMode() { defer shCancel() // Shutdown the HTTP server - err := ws.HTTPServer.Shutdown(shCtx) + err = ws.HTTPServer.Shutdown(shCtx) if err != nil { fmt.Printf("Server shutdown error: %s", err) os.Exit(1) diff --git a/routes.go b/routes.go index eedf5b6..161202b 100644 --- a/routes.go +++ b/routes.go @@ -1,7 +1,14 @@ package main -import "github.com/gorilla/mux" +import ( + "net/http" + + "github.com/gorilla/mux" +) func (s *WebServer) Routes(r *mux.Router) { r.HandleFunc("/version", handleVersion).Methods("GET") + + // Serve generated HTML files from output directory + r.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir(outputPath)))) } diff --git a/watcher.go b/watcher.go new file mode 100644 index 0000000..427df78 --- /dev/null +++ b/watcher.go @@ -0,0 +1,51 @@ +package main + +import ( + "fmt" + + "github.com/fsnotify/fsnotify" +) + +// startWatcher starts watching the content directory for changes +func startWatcher() (*fsnotify.Watcher, error) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return nil, fmt.Errorf("failed to create watcher: %w", err) + } + + // Add content directory to watcher + err = watcher.Add(contentPath) + if err != nil { + watcher.Close() + return nil, fmt.Errorf("failed to watch content directory: %w", err) + } + + fmt.Printf("Watching %s for changes...\n", contentPath) + + // Start goroutine to handle events + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + // Only process write and create events for .md files + if event.Has(fsnotify.Write) || event.Has(fsnotify.Create) { + fmt.Printf("File changed: %s\n", event.Name) + // Regenerate output + if err := generateOutput(); err != nil { + fmt.Printf("Error regenerating: %s\n", err) + } + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + fmt.Printf("Watcher error: %s\n", err) + } + } + }() + + return watcher, nil +}