tangled
alpha
login
or
join now
desertthunder.dev
/
noteleaf
cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists ๐
charm
leaflet
readability
golang
29
fork
atom
overview
issues
2
pulls
pipelines
chore: remove dead code
desertthunder.dev
3 months ago
750e5121
aa33ae54
+25
-126
3 changed files
expand all
collapse all
unified
split
cmd
commands.go
internal
handlers
handlers.go
media_utilities.go
+15
-24
cmd/commands.go
···
9
9
"github.com/stormlightlabs/noteleaf/internal/handlers"
10
10
)
11
11
12
12
-
func parseID(k string, args []string) (int64, error) {
13
13
-
id, err := strconv.ParseInt(args[0], 10, 64)
14
14
-
if err != nil {
15
15
-
return id, fmt.Errorf("invalid %v ID: %s", k, args[0])
16
16
-
}
17
17
-
18
18
-
return id, err
19
19
-
}
20
20
-
21
12
// CommandGroup represents a group of related CLI commands
22
13
type CommandGroup interface {
23
14
Create() *cobra.Command
···
203
194
204
195
Updates episode tracking and completion status. Can mark individual episodes
205
196
or complete seasons/series depending on ID format.`,
206
206
-
Args: cobra.ExactArgs(1),
197
197
+
Args: cobra.ExactArgs(1),
207
198
RunE: func(cmd *cobra.Command, args []string) error {
208
199
return c.handler.MarkWatched(cmd.Context(), args[0])
209
200
},
···
328
319
Specify a percentage value between 0 and 100 to indicate how far you've
329
320
progressed through the book. Automatically updates status to 'reading' if not
330
321
already set.`,
331
331
-
Args: cobra.ExactArgs(2),
322
322
+
Args: cobra.ExactArgs(2),
332
323
RunE: func(cmd *cobra.Command, args []string) error {
333
324
progress, err := strconv.Atoi(args[1])
334
325
if err != nil {
···
345
336
346
337
Valid statuses are: queued (not started), reading (in progress), finished
347
338
(completed), or removed (no longer tracking).`,
348
348
-
Args: cobra.ExactArgs(2),
339
339
+
Args: cobra.ExactArgs(2),
349
340
RunE: func(cmd *cobra.Command, args []string) error {
350
341
return c.handler.UpdateStatus(cmd.Context(), args[0], args[1])
351
342
},
···
359
350
handler *handlers.NoteHandler
360
351
}
361
352
362
362
-
// NewNoteCommand creates a new NoteCommand with the given handler
353
353
+
// NewNoteCommand creates a new [NoteCommand] with the given handler
363
354
func NewNoteCommand(handler *handlers.NoteHandler) *NoteCommand {
364
355
return &NoteCommand{handler: handler}
365
356
}
···
445
436
446
437
Shows the note with syntax highlighting, proper formatting, and metadata.
447
438
Useful for quick viewing without opening an editor.`,
448
448
-
Args: cobra.ExactArgs(1),
439
439
+
Args: cobra.ExactArgs(1),
449
440
RunE: func(cmd *cobra.Command, args []string) error {
450
450
-
if noteID, err := parseID("note", args); err != nil {
441
441
+
if noteID, err := handlers.ParseID(args[0], "note"); err != nil {
451
442
return err
452
443
} else {
453
444
defer c.handler.Close()
···
464
455
Uses the editor specified in your noteleaf configuration or the EDITOR
465
456
environment variable. Changes are automatically saved when you close the
466
457
editor.`,
467
467
-
Args: cobra.ExactArgs(1),
458
458
+
Args: cobra.ExactArgs(1),
468
459
RunE: func(cmd *cobra.Command, args []string) error {
469
469
-
if noteID, err := parseID("note", args); err != nil {
460
460
+
if noteID, err := handlers.ParseID(args[0], "note"); err != nil {
470
461
return err
471
462
} else {
472
463
defer c.handler.Close()
···
483
474
484
475
Removes both the markdown file and database metadata. This operation cannot be
485
476
undone. You will be prompted for confirmation before deletion.`,
486
486
-
Args: cobra.ExactArgs(1),
477
477
+
Args: cobra.ExactArgs(1),
487
478
RunE: func(cmd *cobra.Command, args []string) error {
488
488
-
if noteID, err := parseID("note", args); err != nil {
479
479
+
if noteID, err := handlers.ParseID(args[0], "note"); err != nil {
489
480
return err
490
481
} else {
491
482
defer c.handler.Close()
···
566
557
567
558
Shows article title, author, publication date, URL, and a brief content
568
559
preview. Use 'read' command to view the full article content.`,
569
569
-
Args: cobra.ExactArgs(1),
560
560
+
Args: cobra.ExactArgs(1),
570
561
RunE: func(cmd *cobra.Command, args []string) error {
571
571
-
if articleID, err := parseID("article", args); err != nil {
562
562
+
if articleID, err := handlers.ParseID(args[0], "article"); err != nil {
572
563
return err
573
564
} else {
574
565
defer c.handler.Close()
···
586
577
This displays the complete article content using syntax highlighting and proper formatting.`,
587
578
Args: cobra.ExactArgs(1),
588
579
RunE: func(cmd *cobra.Command, args []string) error {
589
589
-
if articleID, err := parseID("article", args); err != nil {
580
580
+
if articleID, err := handlers.ParseID(args[0], "article"); err != nil {
590
581
return err
591
582
} else {
592
583
defer c.handler.Close()
···
604
595
605
596
Removes the article metadata from the database and deletes associated markdown
606
597
and HTML files. This operation cannot be undone.`,
607
607
-
Args: cobra.ExactArgs(1),
598
598
+
Args: cobra.ExactArgs(1),
608
599
RunE: func(cmd *cobra.Command, args []string) error {
609
609
-
if articleID, err := parseID("article", args); err != nil {
600
600
+
if articleID, err := handlers.ParseID(args[0], "article"); err != nil {
610
601
return err
611
602
} else {
612
603
defer c.handler.Close()
+10
internal/handlers/handlers.go
···
6
6
"io"
7
7
"os"
8
8
"path/filepath"
9
9
+
"strconv"
9
10
10
11
"github.com/stormlightlabs/noteleaf/internal/store"
11
12
"github.com/stormlightlabs/noteleaf/internal/utils"
12
13
)
14
14
+
15
15
+
// ParseID converts a string ID to int64
16
16
+
func ParseID(id string, itemType string) (int64, error) {
17
17
+
itemID, err := strconv.ParseInt(id, 10, 64)
18
18
+
if err != nil {
19
19
+
return 0, fmt.Errorf("invalid %s ID: %s", itemType, id)
20
20
+
}
21
21
+
return itemID, nil
22
22
+
}
13
23
14
24
// Setup initializes the application database and configuration
15
25
func Setup(ctx context.Context, args []string) error {
-102
internal/handlers/media_utilities.go
···
1
1
-
package handlers
2
2
-
3
3
-
import (
4
4
-
"context"
5
5
-
"fmt"
6
6
-
"io"
7
7
-
"strconv"
8
8
-
9
9
-
"github.com/stormlightlabs/noteleaf/internal/models"
10
10
-
)
11
11
-
12
12
-
// MediaPrinter defines how to format a media item for display
13
13
-
type MediaPrinter[T any] func(item *T)
14
14
-
15
15
-
// ListMediaItems is a generic utility for listing media items with status filtering
16
16
-
func ListMediaItems[T any](
17
17
-
ctx context.Context,
18
18
-
status string,
19
19
-
mediaType string,
20
20
-
listAll func(ctx context.Context) ([]*T, error),
21
21
-
listByStatus func(ctx context.Context, status string) ([]*T, error),
22
22
-
printer MediaPrinter[T],
23
23
-
) error {
24
24
-
var items []*T
25
25
-
var err error
26
26
-
27
27
-
if status == "" {
28
28
-
items, err = listAll(ctx)
29
29
-
if err != nil {
30
30
-
return fmt.Errorf("failed to list %s: %w", mediaType, err)
31
31
-
}
32
32
-
} else {
33
33
-
items, err = listByStatus(ctx, status)
34
34
-
if err != nil {
35
35
-
return fmt.Errorf("failed to get %s %s: %w", status, mediaType, err)
36
36
-
}
37
37
-
}
38
38
-
39
39
-
if len(items) == 0 {
40
40
-
if status == "" {
41
41
-
fmt.Printf("No %s found\n", mediaType)
42
42
-
} else {
43
43
-
fmt.Printf("No %s %s found\n", status, mediaType)
44
44
-
}
45
45
-
return nil
46
46
-
}
47
47
-
48
48
-
fmt.Printf("Found %d %s:\n\n", len(items), mediaType)
49
49
-
for _, item := range items {
50
50
-
printer(item)
51
51
-
}
52
52
-
53
53
-
return nil
54
54
-
}
55
55
-
56
56
-
// PromptUserChoice prompts the user to select from a list of results
57
57
-
func PromptUserChoice(reader io.Reader, maxChoices int) (int, error) {
58
58
-
fmt.Print("\nEnter number to add (1-", maxChoices, "), or 0 to cancel: ")
59
59
-
60
60
-
var choice int
61
61
-
if reader != nil {
62
62
-
if _, err := fmt.Fscanf(reader, "%d", &choice); err != nil {
63
63
-
return 0, fmt.Errorf("invalid input")
64
64
-
}
65
65
-
} else {
66
66
-
if _, err := fmt.Scanf("%d", &choice); err != nil {
67
67
-
return 0, fmt.Errorf("invalid input")
68
68
-
}
69
69
-
}
70
70
-
71
71
-
if choice == 0 {
72
72
-
fmt.Println("Cancelled.")
73
73
-
return 0, nil
74
74
-
}
75
75
-
if choice < 1 || choice > maxChoices {
76
76
-
return 0, fmt.Errorf("invalid choice: %d", choice)
77
77
-
}
78
78
-
return choice, nil
79
79
-
}
80
80
-
81
81
-
// ParseID converts a string ID to int64
82
82
-
func ParseID(id string, itemType string) (int64, error) {
83
83
-
itemID, err := strconv.ParseInt(id, 10, 64)
84
84
-
if err != nil {
85
85
-
return 0, fmt.Errorf("invalid %s ID: %s", itemType, id)
86
86
-
}
87
87
-
return itemID, nil
88
88
-
}
89
89
-
90
90
-
// PrintSearchResults displays search results with a type-specific formatter
91
91
-
func PrintSearchResults[T models.Model](results []*models.Model, formatter func(*models.Model, int)) error {
92
92
-
if len(results) == 0 {
93
93
-
fmt.Println("No results found.")
94
94
-
return nil
95
95
-
}
96
96
-
97
97
-
fmt.Printf("Found %d result(s):\n\n", len(results))
98
98
-
for i, result := range results {
99
99
-
formatter(result, i+1)
100
100
-
}
101
101
-
return nil
102
102
-
}