feat: implement serve mode with file watching

- Add fsnotify for file watching in serve mode
- Create watcher.go for content directory monitoring
- Update serve mode to generate initial output and watch for changes
- Add route to serve generated HTML files from output directory
- File changes trigger automatic regeneration

Co-Authored-By: Claude (glm-5) <noreply@anthropic.com>
This commit is contained in:
2026-03-05 02:21:50 +01:00
parent 1d47c5a930
commit e3c6c25624
5 changed files with 84 additions and 3 deletions

6
go.mod
View File

@@ -7,4 +7,8 @@ require (
gopkg.in/yaml.v2 v2.4.0 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
)

4
go.sum
View File

@@ -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 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= 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 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= 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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=

17
main.go
View File

@@ -52,6 +52,21 @@ func runCLIMode() {
func runServeMode() { func runServeMode() {
fmt.Println("Running in Serve mode...") 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 // Create a channel to receive the OS signals
sc := make(chan os.Signal, 1) sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM)
@@ -68,7 +83,7 @@ func runServeMode() {
defer shCancel() defer shCancel()
// Shutdown the HTTP server // Shutdown the HTTP server
err := ws.HTTPServer.Shutdown(shCtx) err = ws.HTTPServer.Shutdown(shCtx)
if err != nil { if err != nil {
fmt.Printf("Server shutdown error: %s", err) fmt.Printf("Server shutdown error: %s", err)
os.Exit(1) os.Exit(1)

View File

@@ -1,7 +1,14 @@
package main package main
import "github.com/gorilla/mux" import (
"net/http"
"github.com/gorilla/mux"
)
func (s *WebServer) Routes(r *mux.Router) { func (s *WebServer) Routes(r *mux.Router) {
r.HandleFunc("/version", handleVersion).Methods("GET") r.HandleFunc("/version", handleVersion).Methods("GET")
// Serve generated HTML files from output directory
r.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir(outputPath))))
} }

51
watcher.go Normal file
View File

@@ -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
}