feat: use glow renderer
This commit is contained in:
123
cmd/list.go
123
cmd/list.go
@@ -4,9 +4,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/yeho/doks/internal/registry"
|
"github.com/yeho/doks/internal/registry"
|
||||||
|
"github.com/yeho/doks/internal/tui"
|
||||||
)
|
)
|
||||||
|
|
||||||
var listTagFilter string
|
var listTagFilter string
|
||||||
@@ -24,7 +26,7 @@ var listCmd = &cobra.Command{
|
|||||||
|
|
||||||
entries := reg.List()
|
entries := reg.List()
|
||||||
if len(entries) == 0 {
|
if len(entries) == 0 {
|
||||||
fmt.Println("No documents registered")
|
fmt.Println(tui.NoResultsStyle.Render("No documents registered"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,24 +47,19 @@ var listCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(entries) == 0 {
|
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
|
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
|
// Print entries
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
symlink := ""
|
printEntry(entry)
|
||||||
if entry.HasSymlink {
|
fmt.Println()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -81,13 +78,97 @@ func hasTag(tags []string, target string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func joinStrings(strs []string, sep string) string {
|
func plural(n int) string {
|
||||||
if len(strs) == 0 {
|
if n == 1 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
result := strs[0]
|
return "s"
|
||||||
for i := 1; i < len(strs); i++ {
|
|
||||||
result += sep + strs[i]
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
24
cmd/root.go
24
cmd/root.go
@@ -156,7 +156,17 @@ func showResult(result *search.Result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func displayFile(filePath string, highlightLine int) {
|
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 {
|
if batPath, err := exec.LookPath("bat"); err == nil {
|
||||||
args := []string{
|
args := []string{
|
||||||
"--paging=never",
|
"--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)
|
content, err := os.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err)
|
||||||
|
|||||||
@@ -48,4 +48,40 @@ var (
|
|||||||
Foreground(secondaryColor).
|
Foreground(secondaryColor).
|
||||||
Italic(true).
|
Italic(true).
|
||||||
MarginTop(1)
|
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)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user