cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm
leaflet
readability
golang
1package main
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "strings"
8
9 "github.com/charmbracelet/fang"
10 "github.com/charmbracelet/log"
11 "github.com/spf13/cobra"
12 "github.com/stormlightlabs/noteleaf/internal/handlers"
13 "github.com/stormlightlabs/noteleaf/internal/store"
14 "github.com/stormlightlabs/noteleaf/internal/ui"
15 "github.com/stormlightlabs/noteleaf/internal/utils"
16)
17
18// App represents the main CLI application
19type App struct {
20 db *store.Database
21 config *store.Config
22}
23
24// NewApp creates a new CLI application instance
25func NewApp() (*App, error) {
26 db, err := store.NewDatabase()
27 if err != nil {
28 return nil, fmt.Errorf("failed to initialize database: %w", err)
29 }
30
31 if config, err := store.LoadConfig(); err != nil {
32 return nil, fmt.Errorf("failed to load configuration: %w", err)
33 } else {
34 return &App{db, config}, nil
35 }
36}
37
38// Close cleans up application resources
39func (app *App) Close() error {
40 if app.db != nil {
41 return app.db.Close()
42 }
43 return nil
44}
45
46func statusCmd() *cobra.Command {
47 return &cobra.Command{
48 Use: "status",
49 Short: "Show application status and configuration",
50 RunE: func(cmd *cobra.Command, args []string) error {
51 return handlers.Status(cmd.Context(), args)
52 },
53 }
54}
55
56func resetCmd() *cobra.Command {
57 return &cobra.Command{
58 Use: "reset",
59 Short: "Reset the application (removes all data)",
60 RunE: func(cmd *cobra.Command, args []string) error {
61 return handlers.Reset(cmd.Context(), args)
62 },
63 }
64}
65
66func rootCmd() *cobra.Command {
67 root := &cobra.Command{
68 Use: "noteleaf",
69 Long: ui.Georgia.ColoredInViewport(),
70 Short: "A TaskWarrior-inspired CLI with notes, media queues and reading lists",
71 RunE: func(c *cobra.Command, args []string) error {
72 if len(args) == 0 {
73 return c.Help()
74 }
75
76 output := strings.Join(args, " ")
77 fmt.Println(output)
78 return nil
79 },
80 }
81
82 root.SetHelpCommand(&cobra.Command{Hidden: true})
83 cobra.EnableCommandSorting = false
84
85 root.AddGroup(&cobra.Group{ID: "core", Title: "Core Commands:"})
86 root.AddGroup(&cobra.Group{ID: "management", Title: "Management Commands:"})
87 return root
88}
89
90func setupCmd() *cobra.Command {
91 handler, err := handlers.NewSeedHandler()
92 if err != nil {
93 log.Fatalf("failed to instantiate seed handler: %v", err)
94 }
95
96 root := &cobra.Command{
97 Use: "setup",
98 Short: "Initialize and manage application setup",
99 RunE: func(c *cobra.Command, args []string) error {
100 return handlers.Setup(c.Context(), args)
101 },
102 }
103
104 seedCmd := &cobra.Command{
105 Use: "seed",
106 Short: "Populate database with test data",
107 Long: "Add sample tasks, books, and notes to the database for testing and demonstration purposes",
108 RunE: func(c *cobra.Command, args []string) error {
109 force, _ := c.Flags().GetBool("force")
110 return handler.Seed(c.Context(), force)
111 },
112 }
113 seedCmd.Flags().BoolP("force", "f", false, "Clear existing data and re-seed")
114
115 root.AddCommand(seedCmd)
116 return root
117}
118
119func confCmd() *cobra.Command {
120 handler, err := handlers.NewConfigHandler()
121 if err != nil {
122 log.Fatalf("failed to create config handler: %v", err)
123 }
124 return NewConfigCommand(handler).Create()
125}
126
127func main() {
128 logger := utils.NewLogger("info", "text")
129 utils.Logger = logger
130
131 app, err := NewApp()
132 if err != nil {
133 logger.Fatal("Failed to initialize application", "error", err)
134 }
135 defer app.Close()
136
137 taskHandler, err := handlers.NewTaskHandler()
138 if err != nil {
139 log.Fatalf("failed to create task handler: %v", err)
140 }
141
142 movieHandler, err := handlers.NewMovieHandler()
143 if err != nil {
144 log.Fatalf("failed to create movie handler: %v", err)
145 }
146
147 tvHandler, err := handlers.NewTVHandler()
148 if err != nil {
149 log.Fatalf("failed to create TV handler: %v", err)
150 }
151
152 noteHandler, err := handlers.NewNoteHandler()
153 if err != nil {
154 log.Fatalf("failed to create note handler: %v", err)
155 }
156
157 bookHandler, err := handlers.NewBookHandler()
158 if err != nil {
159 log.Fatalf("failed to create book handler: %v", err)
160 }
161
162 articleHandler, err := handlers.NewArticleHandler()
163 if err != nil {
164 log.Fatalf("failed to create article handler: %v", err)
165 }
166
167 root := rootCmd()
168
169 coreGroups := []CommandGroup{
170 NewTaskCommand(taskHandler),
171 NewNoteCommand(noteHandler),
172 NewArticleCommand(articleHandler),
173 }
174
175 for _, group := range coreGroups {
176 cmd := group.Create()
177 cmd.GroupID = "core"
178 root.AddCommand(cmd)
179 }
180
181 mediaCmd := &cobra.Command{Use: "media", Short: "Manage media queues (books, movies, TV shows)"}
182 mediaCmd.GroupID = "core"
183 mediaCmd.AddCommand(NewMovieCommand(movieHandler).Create())
184 mediaCmd.AddCommand(NewTVCommand(tvHandler).Create())
185 mediaCmd.AddCommand(NewBookCommand(bookHandler).Create())
186 root.AddCommand(mediaCmd)
187
188 mgmt := []func() *cobra.Command{statusCmd, confCmd, setupCmd, resetCmd}
189 for _, cmdFunc := range mgmt {
190 cmd := cmdFunc()
191 cmd.GroupID = "management"
192 root.AddCommand(cmd)
193 }
194
195 opts := []fang.Option{
196 fang.WithVersion("0.1.0"),
197 fang.WithoutCompletions(),
198 fang.WithColorSchemeFunc(ui.NoteleafColorScheme),
199 }
200
201 if err := fang.Execute(context.Background(), root, opts...); err != nil {
202 os.Exit(1)
203 }
204}