diff --git a/cmd/show.go b/cmd/show.go index 7a9109c..9ae16b6 100644 --- a/cmd/show.go +++ b/cmd/show.go @@ -4,14 +4,17 @@ import ( "fmt" "log" "path/filepath" + "slices" "github.com/notnmeyer/daylog-cli/internal/daylog" "github.com/notnmeyer/daylog-cli/internal/file" "github.com/spf13/cobra" ) -type ShowConfig struct { - Output string +var outputFormats = []string{ + "markdown", "md", + "text", + "web", } var showCmd = &cobra.Command{ @@ -42,6 +45,10 @@ var showCmd = &cobra.Command{ dl.Path = filepath.Join(dl.ProjectPath, prev, "log.md") } + if !validOutputFormat(format) { + log.Fatalf("output must be one of %v\n", outputFormats) + } + logContents, err := dl.Show(format) if err != nil { log.Fatalf("%s", err.Error()) @@ -53,5 +60,13 @@ var showCmd = &cobra.Command{ func init() { rootCmd.AddCommand(showCmd) - showCmd.PersistentFlags().StringP("output", "o", "markdown", "Format output") + showCmd.PersistentFlags().StringP("output", "o", "markdown", fmt.Sprintf("Format output %v", outputFormats)) +} + +func validOutputFormat(format string) bool { + if slices.Contains(outputFormats, format) { + return true + } + + return false } diff --git a/internal/daylog/daylog.go b/internal/daylog/daylog.go index 26d428d..f88cb1c 100644 --- a/internal/daylog/daylog.go +++ b/internal/daylog/daylog.go @@ -1,11 +1,16 @@ package daylog import ( + "encoding/base64" "fmt" + "log" "os" + "os/exec" "path/filepath" + "runtime" "strconv" "strings" + "sync" "time" "github.com/adrg/xdg" @@ -13,6 +18,7 @@ import ( "github.com/notnmeyer/daylog-cli/internal/editor" "github.com/notnmeyer/daylog-cli/internal/git" "github.com/notnmeyer/daylog-cli/internal/output-formatter" + "github.com/notnmeyer/daylog-cli/internal/server" ) type DayLog struct { @@ -77,6 +83,27 @@ func (d *DayLog) Show(format string) (string, error) { return "", err } + if format == "web" { + fmt.Printf("opening \"%s\" in your browser...", d.Path) + + var wg sync.WaitGroup + wg.Add(1) + + // start the server in a goroutine + go server.Start(&wg) + + // build the url and open it in a browser + data := base64.StdEncoding.EncodeToString([]byte(contents)) + url := fmt.Sprintf("http://localhost:8000/show?content=%s", data) + // fmt.Println(url) + open(url) + + // wait until the request has been served + wg.Wait() + + return "", nil + } + contents, err = outputFormatter.Format(format, contents) if err != nil { return "", err @@ -203,3 +230,22 @@ func createIfMissing(d *DayLog) error { return nil } + +// calls "open" or whatever the platform equivilent is on a url +func open(url string) { + var err error + switch runtime.GOOS { + case "linux": + err = exec.Command("xdg-open", url).Start() + case "windows": + err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() + case "darwin": + err = exec.Command("open", url).Start() + default: + log.Fatalf("Unsupported platform") + } + + if err != nil { + log.Fatalf("Error opening URL in default browser: %v", err) + } +} diff --git a/internal/output-formatter/output-formatter.go b/internal/output-formatter/output-formatter.go index 6199155..6ae360c 100644 --- a/internal/output-formatter/output-formatter.go +++ b/internal/output-formatter/output-formatter.go @@ -1,25 +1,16 @@ package outputFormatter import ( - "fmt" - "github.com/charmbracelet/glamour" ) -var MarkdownFormats = []string{ +var OutputFormats = []string{ "markdown", "md", -} - -var OutputFormats = append( - MarkdownFormats, "text", -) + "web", +} func Format(format, content string) (string, error) { - if !contains(OutputFormats, format) { - return "", fmt.Errorf("output must be one of %v\n", OutputFormats) - } - switch format { case "markdown", "md": renderer, _ := glamour.NewTermRenderer( @@ -36,16 +27,3 @@ func Format(format, content string) (string, error) { return content, nil } - -func isMarkdownFormat() { - -} - -func contains(list []string, item string) bool { - for _, val := range list { - if val == item { - return true - } - } - return false -} diff --git a/internal/server/server.go b/internal/server/server.go new file mode 100644 index 0000000..cb529d0 --- /dev/null +++ b/internal/server/server.go @@ -0,0 +1,36 @@ +package server + +import ( + "embed" + "log" + "net/http" + "sync" +) + +//go:embed templates/show.html +var fs embed.FS + +func showHandler(wg *sync.WaitGroup) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + defer wg.Done() + + data, err := fs.ReadFile("templates/show.html") + if err != nil { + http.Error(w, "could not read embedded file", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "text/html") + w.Write(data) + } +} + +func Start(wg *sync.WaitGroup) { + server := &http.Server{Addr: ":8000"} + + http.HandleFunc("/show", showHandler(wg)) + + if err := server.ListenAndServe(); err != http.ErrServerClosed { + log.Fatalf("ListenAndServe(): %v", err) + } +} diff --git a/internal/server/server_test.go b/internal/server/server_test.go new file mode 100644 index 0000000..207c6b5 --- /dev/null +++ b/internal/server/server_test.go @@ -0,0 +1,41 @@ +package server + +import ( + "net/http" + "net/http/httptest" + "strings" + "sync" + "testing" +) + +func TestShowHandler(t *testing.T) { + var wg sync.WaitGroup + wg.Add(1) + + req := httptest.NewRequest("GET", "/show", nil) + w := httptest.NewRecorder() + + handler := showHandler(&wg) + handler.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Errorf("expected status %d, got %d", http.StatusOK, w.Code) + } + + expectedContentType := "text/html" + if contentType := w.Header().Get("Content-Type"); contentType != expectedContentType { + t.Errorf("expected Content-Type %s, got %s", expectedContentType, contentType) + } + + body := w.Body.String() + if !strings.Contains(body, "") { + t.Error("expected HTML content, got non-HTML response") + } + + // we could be more specific but whatever + if !strings.Contains(body, "Daylog") { + t.Error("expected title 'Daylog' in response") + } + + wg.Wait() +} diff --git a/internal/server/templates/show.html b/internal/server/templates/show.html new file mode 100644 index 0000000..bfb87bc --- /dev/null +++ b/internal/server/templates/show.html @@ -0,0 +1,45 @@ + + +
+ + +