package cmd import ( "fmt" "os" "sort" "time" "github.com/spf13/cobra" "github.com/yeho/doks/internal/registry" "github.com/yeho/doks/internal/tui" ) var listTagFilter string var listCmd = &cobra.Command{ Use: "list", Short: "List all registered documents", Long: `Display all documents registered in doks, optionally filtered by tag.`, Run: func(cmd *cobra.Command, args []string) { reg, err := registry.Load(cfg.RegistryPath()) if err != nil { fmt.Fprintf(os.Stderr, "Error loading registry: %v\n", err) os.Exit(1) } entries := reg.List() if len(entries) == 0 { fmt.Println(tui.NoResultsStyle.Render("No documents registered")) return } // Sort by key sort.Slice(entries, func(i, j int) bool { return entries[i].Key < entries[j].Key }) // Filter by tag if specified if listTagFilter != "" { filtered := make([]*registry.Entry, 0) for _, entry := range entries { if hasTag(entry.Tags, listTagFilter) { filtered = append(filtered, entry) } } entries = filtered } if len(entries) == 0 { 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 { printEntry(entry) fmt.Println() } }, } func init() { rootCmd.AddCommand(listCmd) listCmd.Flags().StringVarP(&listTagFilter, "tag", "t", "", "Filter by tag") } func hasTag(tags []string, target string) bool { for _, tag := range tags { if tag == target { return true } } return false } func plural(n int) string { if n == 1 { return "" } 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") } }