cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm leaflet readability golang
29
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 7da458848ced66d8eab8b2e86c031c8dc4e0e6fc 442 lines 12 kB view raw
1package main 2 3import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 "github.com/charmbracelet/log" 9 10 "github.com/spf13/cobra" 11 "github.com/stormlightlabs/noteleaf/internal/handlers" 12 "github.com/stormlightlabs/noteleaf/internal/ui" 13) 14 15func rootCmd() *cobra.Command { 16 return &cobra.Command{ 17 Use: "noteleaf", 18 Long: ui.Georgia.ColoredInViewport(), 19 Short: "A TaskWarrior-inspired CLI with notes, media queues and reading lists", 20 RunE: func(cmd *cobra.Command, args []string) error { 21 if len(args) == 0 { 22 return cmd.Help() 23 } 24 25 output := strings.Join(args, " ") 26 fmt.Println(output) 27 return nil 28 }, 29 } 30} 31 32func todoCmd() *cobra.Command { 33 root := &cobra.Command{ 34 Use: "todo", 35 Short: "task management", 36 } 37 38 root.AddCommand(&cobra.Command{ 39 Use: "add [description]", 40 Short: "Add a new task", 41 Aliases: []string{"create", "new"}, 42 Args: cobra.MinimumNArgs(1), 43 RunE: func(cmd *cobra.Command, args []string) error { 44 return handlers.CreateTask(cmd.Context(), args) 45 }, 46 }) 47 48 root.AddCommand(&cobra.Command{ 49 Use: "list", 50 Short: "List tasks", 51 Aliases: []string{"ls"}, 52 RunE: func(cmd *cobra.Command, args []string) error { 53 return handlers.ListTasks(cmd.Context(), args) 54 }, 55 }) 56 57 root.AddCommand(&cobra.Command{ 58 Use: "view [task-id]", 59 Short: "View task by ID", 60 Args: cobra.ExactArgs(1), 61 RunE: func(cmd *cobra.Command, args []string) error { 62 return handlers.ViewTask(cmd.Context(), args) 63 }, 64 }) 65 66 root.AddCommand(&cobra.Command{ 67 Use: "update [task-id] [options...]", 68 Short: "Update task properties", 69 Args: cobra.MinimumNArgs(1), 70 RunE: func(cmd *cobra.Command, args []string) error { 71 return handlers.UpdateTask(cmd.Context(), args) 72 }, 73 }) 74 75 root.AddCommand(&cobra.Command{ 76 Use: "delete [task-id]", 77 Short: "Delete a task", 78 Args: cobra.ExactArgs(1), 79 RunE: func(cmd *cobra.Command, args []string) error { 80 return handlers.DeleteTask(cmd.Context(), args) 81 }, 82 }) 83 84 root.AddCommand(&cobra.Command{ 85 Use: "projects", 86 Short: "List projects", 87 Aliases: []string{"proj"}, 88 RunE: func(cmd *cobra.Command, args []string) error { 89 fmt.Println("Listing projects...") 90 return nil 91 }, 92 }) 93 94 root.AddCommand(&cobra.Command{ 95 Use: "tags", 96 Short: "List tags", 97 Aliases: []string{"t"}, 98 RunE: func(cmd *cobra.Command, args []string) error { 99 fmt.Println("Listing tags...") 100 return nil 101 }, 102 }) 103 104 root.AddCommand(&cobra.Command{ 105 Use: "contexts", 106 Short: "List contexts (locations)", 107 Aliases: []string{"loc", "ctx", "locations"}, 108 RunE: func(cmd *cobra.Command, args []string) error { 109 fmt.Println("Listing task contexts...") 110 return nil 111 }, 112 }) 113 114 root.AddCommand(&cobra.Command{ 115 Use: "done [task-id]", 116 Short: "Mark task as completed", 117 Aliases: []string{"complete"}, 118 Args: cobra.ExactArgs(1), 119 RunE: func(cmd *cobra.Command, args []string) error { 120 return handlers.DoneTask(cmd.Context(), args) 121 }, 122 }) 123 124 return root 125} 126 127func movieCmd() *cobra.Command { 128 root := &cobra.Command{ 129 Use: "movie", 130 Short: "Manage movie watch queue", 131 } 132 133 root.AddCommand(&cobra.Command{ 134 Use: "add [title]", 135 Short: "Add movie to watch queue", 136 Args: cobra.MinimumNArgs(1), 137 RunE: func(cmd *cobra.Command, args []string) error { 138 title := args[0] 139 fmt.Printf("Adding movie: %s\n", title) 140 // TODO: Implement movie addition 141 return nil 142 }, 143 }) 144 145 root.AddCommand(&cobra.Command{ 146 Use: "list", 147 Short: "List movies in queue", 148 RunE: func(cmd *cobra.Command, args []string) error { 149 fmt.Println("Listing movies...") 150 // TODO: Implement movie listing 151 return nil 152 }, 153 }) 154 155 return root 156} 157 158func tvCmd() *cobra.Command { 159 root := &cobra.Command{ 160 Use: "tv", 161 Short: "Manage TV show watch queue", 162 } 163 164 root.AddCommand(&cobra.Command{ 165 Use: "add [title]", 166 Short: "Add TV show to watch queue", 167 Args: cobra.MinimumNArgs(1), 168 RunE: func(cmd *cobra.Command, args []string) error { 169 title := args[0] 170 fmt.Printf("Adding TV show: %s\n", title) 171 // TODO: Implement TV show addition 172 return nil 173 }, 174 }) 175 176 root.AddCommand(&cobra.Command{ 177 Use: "list", 178 Short: "List TV shows in queue", 179 RunE: func(cmd *cobra.Command, args []string) error { 180 fmt.Println("Listing TV shows...") 181 // TODO: Implement TV show listing 182 return nil 183 }, 184 }) 185 186 return root 187} 188 189func bookCmd() *cobra.Command { 190 root := &cobra.Command{ 191 Use: "book", 192 Short: "Manage reading list", 193 } 194 195 // book add - Search and add book to reading list 196 addCmd := &cobra.Command{ 197 Use: "add [search query...]", 198 Short: "Search and add book to reading list", 199 Long: `Search for books and add them to your reading list. 200 201By default, shows search results in a simple list format where you can select by number. 202Use the -i flag for an interactive interface with navigation keys.`, 203 RunE: func(cmd *cobra.Command, args []string) error { 204 interactive, _ := cmd.Flags().GetBool("interactive") 205 return handlers.SearchAndAddWithOptions(cmd.Context(), args, interactive) 206 }, 207 } 208 addCmd.Flags().BoolP("interactive", "i", false, "Use interactive interface for book selection") 209 root.AddCommand(addCmd) 210 211 // book list - Show reading queue with progress 212 root.AddCommand(&cobra.Command{ 213 Use: "list [--all|--reading|--finished|--queued]", 214 Short: "Show reading queue with progress", 215 RunE: func(cmd *cobra.Command, args []string) error { 216 return handlers.ListBooks(cmd.Context(), args) 217 }, 218 }) 219 220 // book reading - Mark book as currently reading (alias for update status) 221 root.AddCommand(&cobra.Command{ 222 Use: "reading <id>", 223 Short: "Mark book as currently reading", 224 Args: cobra.ExactArgs(1), 225 RunE: func(cmd *cobra.Command, args []string) error { 226 return handlers.UpdateBookStatus(cmd.Context(), []string{args[0], "reading"}) 227 }, 228 }) 229 230 // book finished - Mark book as completed 231 root.AddCommand(&cobra.Command{ 232 Use: "finished <id>", 233 Short: "Mark book as completed", 234 Aliases: []string{"read"}, 235 Args: cobra.ExactArgs(1), 236 RunE: func(cmd *cobra.Command, args []string) error { 237 return handlers.UpdateBookStatus(cmd.Context(), []string{args[0], "finished"}) 238 }, 239 }) 240 241 // book remove - Remove from reading list 242 root.AddCommand(&cobra.Command{ 243 Use: "remove <id>", 244 Short: "Remove from reading list", 245 Aliases: []string{"rm"}, 246 Args: cobra.ExactArgs(1), 247 RunE: func(cmd *cobra.Command, args []string) error { 248 return handlers.UpdateBookStatus(cmd.Context(), []string{args[0], "removed"}) 249 }, 250 }) 251 252 // book progress - Update reading progress percentage 253 root.AddCommand(&cobra.Command{ 254 Use: "progress <id> <percentage>", 255 Short: "Update reading progress percentage (0-100)", 256 Args: cobra.ExactArgs(2), 257 RunE: func(cmd *cobra.Command, args []string) error { 258 return handlers.UpdateBookProgress(cmd.Context(), args) 259 }, 260 }) 261 262 // book update - Update book status 263 root.AddCommand(&cobra.Command{ 264 Use: "update <id> <status>", 265 Short: "Update book status (queued|reading|finished|removed)", 266 Args: cobra.ExactArgs(2), 267 RunE: func(cmd *cobra.Command, args []string) error { 268 return handlers.UpdateBookStatus(cmd.Context(), args) 269 }, 270 }) 271 272 return root 273} 274 275func noteCmd() *cobra.Command { 276 root := &cobra.Command{ 277 Use: "note", 278 Short: "Manage notes", 279 } 280 281 handler, err := handlers.NewNoteHandler() 282 if err != nil { 283 log.Fatalf("failed to instantiate note handler: %v", err) 284 } 285 286 createCmd := &cobra.Command{ 287 Use: "create [title] [content...]", 288 Short: "Create a new note", 289 Aliases: []string{"new"}, 290 RunE: func(cmd *cobra.Command, args []string) error { 291 interactive, _ := cmd.Flags().GetBool("interactive") 292 filePath, _ := cmd.Flags().GetString("file") 293 294 var title, content string 295 if len(args) > 0 { 296 title = args[0] 297 } 298 if len(args) > 1 { 299 content = strings.Join(args[1:], " ") 300 } 301 302 if err != nil { 303 return err 304 } 305 defer handler.Close() 306 return handler.Create(cmd.Context(), title, content, filePath, interactive) 307 }, 308 } 309 createCmd.Flags().BoolP("interactive", "i", false, "Open interactive editor") 310 createCmd.Flags().StringP("file", "f", "", "Create note from markdown file") 311 root.AddCommand(createCmd) 312 313 listCmd := &cobra.Command{ 314 Use: "list [--archived] [--tags=tag1,tag2]", 315 Short: "Opens interactive TUI browser for navigating and viewing notes", 316 Aliases: []string{"ls"}, 317 RunE: func(cmd *cobra.Command, args []string) error { 318 archived, _ := cmd.Flags().GetBool("archived") 319 tagsStr, _ := cmd.Flags().GetString("tags") 320 321 var tags []string 322 if tagsStr != "" { 323 tags = strings.Split(tagsStr, ",") 324 for i := range tags { 325 tags[i] = strings.TrimSpace(tags[i]) 326 } 327 } 328 329 handler, err := handlers.NewNoteHandler() 330 if err != nil { 331 return err 332 } 333 defer handler.Close() 334 return handler.List(cmd.Context(), false, archived, tags) 335 }, 336 } 337 listCmd.Flags().BoolP("archived", "a", false, "Show archived notes") 338 listCmd.Flags().String("tags", "", "Filter by tags (comma-separated)") 339 root.AddCommand(listCmd) 340 341 root.AddCommand(&cobra.Command{ 342 Use: "read [note-id]", 343 Short: "Display formatted note content with syntax highlighting", 344 Aliases: []string{"view"}, 345 Args: cobra.ExactArgs(1), 346 RunE: func(cmd *cobra.Command, args []string) error { 347 noteID, err := strconv.ParseInt(args[0], 10, 64) 348 if err != nil { 349 return fmt.Errorf("invalid note ID: %s", args[0]) 350 } 351 handler, err := handlers.NewNoteHandler() 352 if err != nil { 353 return err 354 } 355 defer handler.Close() 356 return handler.View(cmd.Context(), noteID) 357 }, 358 }) 359 360 root.AddCommand(&cobra.Command{ 361 Use: "edit [note-id]", 362 Short: "Edit note in configured editor", 363 Args: cobra.ExactArgs(1), 364 RunE: func(cmd *cobra.Command, args []string) error { 365 noteID, err := strconv.ParseInt(args[0], 10, 64) 366 if err != nil { 367 return fmt.Errorf("invalid note ID: %s", args[0]) 368 } 369 handler, err := handlers.NewNoteHandler() 370 if err != nil { 371 return err 372 } 373 defer handler.Close() 374 return handler.Edit(cmd.Context(), noteID) 375 }, 376 }) 377 378 root.AddCommand(&cobra.Command{ 379 Use: "remove [note-id]", 380 Short: "Permanently removes the note file and metadata", 381 Aliases: []string{"rm", "delete", "del"}, 382 Args: cobra.ExactArgs(1), 383 RunE: func(cmd *cobra.Command, args []string) error { 384 noteID, err := strconv.ParseInt(args[0], 10, 64) 385 if err != nil { 386 return fmt.Errorf("invalid note ID: %s", args[0]) 387 } 388 handler, err := handlers.NewNoteHandler() 389 if err != nil { 390 return err 391 } 392 defer handler.Close() 393 return handler.Delete(cmd.Context(), noteID) 394 }, 395 }) 396 397 return root 398} 399 400func statusCmd() *cobra.Command { 401 return &cobra.Command{ 402 Use: "status", 403 Short: "Show application status and configuration", 404 RunE: func(cmd *cobra.Command, args []string) error { 405 return handlers.Status(cmd.Context(), args) 406 }, 407 } 408} 409 410func resetCmd() *cobra.Command { 411 return &cobra.Command{ 412 Use: "reset", 413 Short: "Reset the application (removes all data)", 414 RunE: func(cmd *cobra.Command, args []string) error { 415 return handlers.Reset(cmd.Context(), args) 416 }, 417 } 418} 419 420func setupCmd() *cobra.Command { 421 return &cobra.Command{ 422 Use: "setup", 423 Short: "Initialize the application database and configuration", 424 RunE: func(cmd *cobra.Command, args []string) error { 425 return handlers.Setup(cmd.Context(), args) 426 }, 427 } 428} 429 430func confCmd() *cobra.Command { 431 return &cobra.Command{ 432 Use: "config [key] [value]", 433 Short: "Manage configuration", 434 Args: cobra.ExactArgs(2), 435 RunE: func(cmd *cobra.Command, args []string) error { 436 key, value := args[0], args[1] 437 fmt.Printf("Setting config %s = %s\n", key, value) 438 // TODO: Implement config management 439 return nil 440 }, 441 } 442}