From d24e4f340e543edbde06516a7a502c9082e71cd5 Mon Sep 17 00:00:00 2001 From: ysandler Date: Sun, 1 Feb 2026 18:46:18 -0600 Subject: [PATCH] feat: use glow renderer --- VERSION | 2 +- cmd/list.go | 123 ++++++++++++++++++++++++++++++++++------- cmd/root.go | 24 +++++++- internal/tui/styles.go | 36 ++++++++++++ 4 files changed, 161 insertions(+), 24 deletions(-) diff --git a/VERSION b/VERSION index 6e8bf73..17e51c3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0 +0.1.1 diff --git a/cmd/list.go b/cmd/list.go index 1e4cc4d..1d20590 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -4,9 +4,11 @@ import ( "fmt" "os" "sort" + "time" "github.com/spf13/cobra" "github.com/yeho/doks/internal/registry" + "github.com/yeho/doks/internal/tui" ) var listTagFilter string @@ -24,7 +26,7 @@ var listCmd = &cobra.Command{ entries := reg.List() if len(entries) == 0 { - fmt.Println("No documents registered") + fmt.Println(tui.NoResultsStyle.Render("No documents registered")) return } @@ -45,24 +47,19 @@ var listCmd = &cobra.Command{ } if len(entries) == 0 { - fmt.Printf("No documents found with tag '%s'\n", listTagFilter) + fmt.Printf(tui.NoResultsStyle.Render("No documents found with tag '%s'\n"), listTagFilter) return } + // Print section header + fmt.Println(tui.ListTitleStyle.Render("📚 Registered Documents")) + fmt.Println(tui.CountStyle.Render(fmt.Sprintf("(%d document%s total)", len(entries), plural(len(entries))))) + fmt.Println() + // Print entries for _, entry := range entries { - symlink := "" - if entry.HasSymlink { - symlink = " [symlinked]" - } - tags := "" - if len(entry.Tags) > 0 { - tags = fmt.Sprintf(" [%s]", joinStrings(entry.Tags, ", ")) - } - fmt.Printf(" %s%s%s\n", entry.Key, tags, symlink) - if entry.Description != "" { - fmt.Printf(" %s\n", entry.Description) - } + printEntry(entry) + fmt.Println() } }, } @@ -81,13 +78,97 @@ func hasTag(tags []string, target string) bool { return false } -func joinStrings(strs []string, sep string) string { - if len(strs) == 0 { +func plural(n int) string { + if n == 1 { return "" } - result := strs[0] - for i := 1; i < len(strs); i++ { - result += sep + strs[i] - } - return result + return "s" } + +func printEntry(entry *registry.Entry) { + // Entry key + fmt.Print(tui.SymlinkIcon) + + if entry.HasSymlink { + fmt.Print(tui.EntryKeyStyle.Render(entry.Key)) + fmt.Print(" ") + fmt.Print(tui.SymlinkIndicator) + } else { + fmt.Print(tui.EntryKeyStyle.Render(entry.Key)) + } + + // Filename + fmt.Print(" ") + fmt.Print(tui.FilenameStyle.Render(entry.Filename)) + + // Tags + if len(entry.Tags) > 0 { + tagsStr := "" + for i, tag := range entry.Tags { + if i > 0 { + tagsStr += ", " + } + tagsStr += tui.TagsStyle.Render(tag) + } + fmt.Print(" ") + fmt.Print(tagsStr) + } + + // Status indicator + if entry.HasSymlink { + fmt.Print(" ") + fmt.Print(tui.TagsStyle.Render("[symlinked]")) + } + + fmt.Println() + + // Description + if entry.Description != "" { + fmt.Println(tui.DescriptionStyle.Render(entry.Description)) + } + + // Metadata + if entry.CreatedAt.IsZero() && entry.UpdatedAt.IsZero() { + return + } + + var createdStr, updatedStr string + if !entry.CreatedAt.IsZero() { + createdStr = fmt.Sprintf("Created: %s", formatTime(entry.CreatedAt)) + } + if !entry.UpdatedAt.IsZero() { + updatedStr = fmt.Sprintf("Updated: %s", formatTime(entry.UpdatedAt)) + } + + var metaStr string + if createdStr != "" && updatedStr != "" { + metaStr = createdStr + " • " + updatedStr + } else { + metaStr = createdStr + updatedStr + } + if metaStr != "" { + fmt.Println(tui.MetadataStyle.Render(metaStr)) + } +} + +func formatTime(t time.Time) string { + now := time.Now() + diff := now.Sub(t) + + switch { + case diff.Seconds() < 60: + return "just now" + case diff.Hours() < 24: + return fmt.Sprintf("%.0f hours ago", diff.Hours()) + case diff.Hours() < 48: + return "yesterday" + case diff.Hours() < 7*24: + return fmt.Sprintf("%.0f days ago", diff.Hours()/24) + case diff.Hours() < 30*24: + return fmt.Sprintf("%.0f weeks ago", diff.Hours()/(7*24)) + case diff.Hours() < 365*24: + return fmt.Sprintf("%.0f months ago", diff.Hours()/(30*24)) + default: + return t.Format("Jan 2, 2006") + } +} \ No newline at end of file diff --git a/cmd/root.go b/cmd/root.go index 3d86ce5..8674ede 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -156,7 +156,17 @@ func showResult(result *search.Result) { } func displayFile(filePath string, highlightLine int) { - // Try bat first for syntax highlighting + // Try glow first for Markdown rendering + if glowPath, err := exec.LookPath("glow"); err == nil { + cmd := exec.Command(glowPath, filePath) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err == nil { + return + } + } + + // Fallback to bat for syntax highlighting if batPath, err := exec.LookPath("bat"); err == nil { args := []string{ "--paging=never", @@ -175,7 +185,17 @@ func displayFile(filePath string, highlightLine int) { } } - // Fallback to plain output + // Fallback to cat + if catPath, err := exec.LookPath("cat"); err == nil { + cmd := exec.Command(catPath, filePath) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err == nil { + return + } + } + + // Final fallback to plain Go file read content, err := os.ReadFile(filePath) if err != nil { fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err) diff --git a/internal/tui/styles.go b/internal/tui/styles.go index 592b5e9..61276c3 100644 --- a/internal/tui/styles.go +++ b/internal/tui/styles.go @@ -48,4 +48,40 @@ var ( Foreground(secondaryColor). Italic(true). MarginTop(1) + + // List command styles + ListTitleStyle = lipgloss.NewStyle(). + Bold(true). + Foreground(lipgloss.Color("213")). // Magenta/pink + MarginBottom(1) + + CountStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("244")). // Dimmed gray + Italic(true) + + EntryKeyStyle = lipgloss.NewStyle(). + Bold(true). + Foreground(lipgloss.Color("154")) // Bright green + + EntryKeySelected = lipgloss.NewStyle(). + Bold(true). + Foreground(lipgloss.Color("86")) // Cyan + + FilenameStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("241")) // Gray + + TagsStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("226")) // Yellow + + SymlinkIcon = "➜ " + + SymlinkIndicator = "🔗" + + DescriptionStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("243")). // Dimmed text + PaddingLeft(2) + + MetadataStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("245")). // Dimmed gray + PaddingLeft(4) )