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
build: reorganize main
desertthunder.dev
5 months ago
f1b9a48d
d9ec13f0
+249
-205
5 changed files
expand all
collapse all
unified
split
cmd
cli
commands.go
main.go
codecov.yml
internal
ui
logo.go
ui_test.go
+225
cmd/cli/commands.go
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
package main
2
+
3
+
import (
4
+
"fmt"
5
+
"strings"
6
+
7
+
"github.com/spf13/cobra"
8
+
"github.com/stormlightlabs/noteleaf/cmd/handlers"
9
+
"github.com/stormlightlabs/noteleaf/internal/ui"
10
+
)
11
+
12
+
func rootCmd() *cobra.Command {
13
+
return &cobra.Command{
14
+
Use: "noteleaf",
15
+
Long: ui.Colossal.ColoredInViewport(),
16
+
Short: "A TaskWarrior-inspired CLI with notes, media queues and reading lists",
17
+
Run: func(cmd *cobra.Command, args []string) {
18
+
if len(args) == 0 {
19
+
cmd.Help()
20
+
} else {
21
+
output := strings.Join(args, " ")
22
+
fmt.Println(output)
23
+
}
24
+
},
25
+
}
26
+
}
27
+
28
+
func todoCmd() *cobra.Command {
29
+
root := &cobra.Command{
30
+
Use: "todo",
31
+
Short: "task management",
32
+
}
33
+
34
+
root.AddCommand(&cobra.Command{
35
+
Use: "add [description]",
36
+
Short: "Add a new task",
37
+
Args: cobra.MinimumNArgs(1),
38
+
RunE: func(cmd *cobra.Command, args []string) error {
39
+
description := args[0]
40
+
fmt.Printf("Adding task: %s\n", description)
41
+
// TODO: Implement task creation
42
+
return nil
43
+
},
44
+
})
45
+
46
+
root.AddCommand(&cobra.Command{
47
+
Use: "list",
48
+
Short: "List tasks",
49
+
Aliases: []string{"ls"},
50
+
RunE: func(cmd *cobra.Command, args []string) error {
51
+
fmt.Println("Listing tasks...")
52
+
// TODO: Implement task listing
53
+
return nil
54
+
},
55
+
})
56
+
57
+
root.AddCommand(&cobra.Command{
58
+
Use: "done [task-id]",
59
+
Short: "Mark task as completed",
60
+
Args: cobra.ExactArgs(1),
61
+
RunE: func(cmd *cobra.Command, args []string) error {
62
+
taskID := args[0]
63
+
fmt.Printf("Marking task %s as done\n", taskID)
64
+
// TODO: Implement task completion
65
+
return nil
66
+
},
67
+
})
68
+
69
+
return root
70
+
}
71
+
72
+
func movieCmd() *cobra.Command {
73
+
root := &cobra.Command{
74
+
Use: "movie",
75
+
Short: "Manage movie watch queue",
76
+
}
77
+
78
+
root.AddCommand(&cobra.Command{
79
+
Use: "add [title]",
80
+
Short: "Add movie to watch queue",
81
+
Args: cobra.MinimumNArgs(1),
82
+
RunE: func(cmd *cobra.Command, args []string) error {
83
+
title := args[0]
84
+
fmt.Printf("Adding movie: %s\n", title)
85
+
// TODO: Implement movie addition
86
+
return nil
87
+
},
88
+
})
89
+
90
+
root.AddCommand(&cobra.Command{
91
+
Use: "list",
92
+
Short: "List movies in queue",
93
+
RunE: func(cmd *cobra.Command, args []string) error {
94
+
fmt.Println("Listing movies...")
95
+
// TODO: Implement movie listing
96
+
return nil
97
+
},
98
+
})
99
+
100
+
return root
101
+
}
102
+
103
+
func tvCmd() *cobra.Command {
104
+
root := &cobra.Command{
105
+
Use: "tv",
106
+
Short: "Manage TV show watch queue",
107
+
}
108
+
109
+
root.AddCommand(&cobra.Command{
110
+
Use: "add [title]",
111
+
Short: "Add TV show to watch queue",
112
+
Args: cobra.MinimumNArgs(1),
113
+
RunE: func(cmd *cobra.Command, args []string) error {
114
+
title := args[0]
115
+
fmt.Printf("Adding TV show: %s\n", title)
116
+
// TODO: Implement TV show addition
117
+
return nil
118
+
},
119
+
})
120
+
121
+
root.AddCommand(&cobra.Command{
122
+
Use: "list",
123
+
Short: "List TV shows in queue",
124
+
RunE: func(cmd *cobra.Command, args []string) error {
125
+
fmt.Println("Listing TV shows...")
126
+
// TODO: Implement TV show listing
127
+
return nil
128
+
},
129
+
})
130
+
131
+
return root
132
+
}
133
+
134
+
func bookCmd() *cobra.Command {
135
+
root := &cobra.Command{
136
+
Use: "book",
137
+
Short: "Manage reading list",
138
+
}
139
+
140
+
root.AddCommand(&cobra.Command{
141
+
Use: "add [title]",
142
+
Short: "Add book to reading list",
143
+
Args: cobra.MinimumNArgs(1),
144
+
RunE: func(cmd *cobra.Command, args []string) error {
145
+
title := args[0]
146
+
fmt.Printf("Adding book: %s\n", title)
147
+
// TODO: Implement book addition
148
+
return nil
149
+
},
150
+
})
151
+
152
+
root.AddCommand(&cobra.Command{
153
+
Use: "list",
154
+
Short: "List books in reading list",
155
+
RunE: func(cmd *cobra.Command, args []string) error {
156
+
fmt.Println("Listing books...")
157
+
// TODO: Implement book listing
158
+
return nil
159
+
},
160
+
})
161
+
162
+
return root
163
+
}
164
+
165
+
func noteCmd() *cobra.Command {
166
+
root := &cobra.Command{
167
+
Use: "note",
168
+
Short: "Manage notes",
169
+
}
170
+
171
+
root.AddCommand(&cobra.Command{
172
+
Use: "create [title] [content...]",
173
+
Short: "Create a new note",
174
+
Aliases: []string{"new"},
175
+
RunE: func(cmd *cobra.Command, args []string) error {
176
+
return handlers.Create(cmd.Context(), args)
177
+
},
178
+
})
179
+
180
+
return root
181
+
}
182
+
183
+
func statusCmd() *cobra.Command {
184
+
return &cobra.Command{
185
+
Use: "status",
186
+
Short: "Show application status and configuration",
187
+
RunE: func(cmd *cobra.Command, args []string) error {
188
+
return handlers.Status(cmd.Context(), args)
189
+
},
190
+
}
191
+
}
192
+
193
+
func resetCmd() *cobra.Command {
194
+
return &cobra.Command{
195
+
Use: "reset",
196
+
Short: "Reset the application (removes all data)",
197
+
RunE: func(cmd *cobra.Command, args []string) error {
198
+
return handlers.Reset(cmd.Context(), args)
199
+
},
200
+
}
201
+
}
202
+
203
+
func setupCmd() *cobra.Command {
204
+
return &cobra.Command{
205
+
Use: "setup",
206
+
Short: "Initialize the application database and configuration",
207
+
RunE: func(cmd *cobra.Command, args []string) error {
208
+
return handlers.Setup(cmd.Context(), args)
209
+
},
210
+
}
211
+
}
212
+
213
+
func confCmd() *cobra.Command {
214
+
return &cobra.Command{
215
+
Use: "config [key] [value]",
216
+
Short: "Manage configuration",
217
+
Args: cobra.ExactArgs(2),
218
+
RunE: func(cmd *cobra.Command, args []string) error {
219
+
key, value := args[0], args[1]
220
+
fmt.Printf("Setting config %s = %s\n", key, value)
221
+
// TODO: Implement config management
222
+
return nil
223
+
},
224
+
}
225
+
}
+15
-193
cmd/cli/main.go
···
4
"context"
5
"fmt"
6
"os"
7
-
"strings"
8
9
"github.com/charmbracelet/fang"
10
"github.com/spf13/cobra"
11
-
"github.com/stormlightlabs/noteleaf/cmd/handlers"
12
"github.com/stormlightlabs/noteleaf/internal/store"
13
"github.com/stormlightlabs/noteleaf/internal/ui"
14
"github.com/stormlightlabs/noteleaf/internal/utils"
···
27
return nil, fmt.Errorf("failed to initialize database: %w", err)
28
}
29
30
-
config, err := store.LoadConfig()
31
-
if err != nil {
32
return nil, fmt.Errorf("failed to load configuration: %w", err)
0
0
33
}
34
-
35
-
return &App{
36
-
db: db,
37
-
config: config,
38
-
}, nil
39
}
40
41
// Close cleans up application resources
···
56
}
57
defer app.Close()
58
59
-
rootCmd := &cobra.Command{
60
-
Use: "noteleaf",
61
-
Short: "A TaskWarrior-inspired CLI with media queues and reading lists",
62
-
Run: func(cmd *cobra.Command, args []string) {
63
-
if len(args) == 0 {
64
-
fmt.Println(ui.Collosal.ColoredInViewport())
65
-
cmd.Help()
66
-
return
67
-
}
68
-
69
-
output := strings.Join(args, " ")
70
-
fmt.Println(output)
71
-
},
72
-
}
73
-
74
-
rootCmd.AddCommand(&cobra.Command{
75
-
Use: "setup",
76
-
Short: "Initialize the application database and configuration",
77
-
RunE: func(cmd *cobra.Command, args []string) error {
78
-
return handlers.Setup(cmd.Context(), args)
79
-
},
80
-
})
81
-
82
-
rootCmd.AddCommand(&cobra.Command{
83
-
Use: "reset",
84
-
Short: "Reset the application (removes all data)",
85
-
RunE: func(cmd *cobra.Command, args []string) error {
86
-
return handlers.Reset(cmd.Context(), args)
87
-
},
88
-
})
89
-
90
-
rootCmd.AddCommand(&cobra.Command{
91
-
Use: "status",
92
-
Short: "Show application status and configuration",
93
-
RunE: func(cmd *cobra.Command, args []string) error {
94
-
return handlers.Status(cmd.Context(), args)
95
-
},
96
-
})
97
-
98
-
rootCmd.AddCommand(&cobra.Command{
99
-
Use: "add [description]",
100
-
Short: "Add a new task",
101
-
Args: cobra.MinimumNArgs(1),
102
-
RunE: func(cmd *cobra.Command, args []string) error {
103
-
description := args[0]
104
-
fmt.Printf("Adding task: %s\n", description)
105
-
// TODO: Implement task creation
106
-
return nil
107
-
},
108
-
})
109
-
110
-
rootCmd.AddCommand(&cobra.Command{
111
-
Use: "list",
112
-
Short: "List tasks",
113
-
Aliases: []string{"ls"},
114
-
RunE: func(cmd *cobra.Command, args []string) error {
115
-
fmt.Println("Listing tasks...")
116
-
// TODO: Implement task listing
117
-
return nil
118
-
},
119
-
})
120
-
121
-
rootCmd.AddCommand(&cobra.Command{
122
-
Use: "done [task-id]",
123
-
Short: "Mark task as completed",
124
-
Args: cobra.ExactArgs(1),
125
-
RunE: func(cmd *cobra.Command, args []string) error {
126
-
taskID := args[0]
127
-
fmt.Printf("Marking task %s as done\n", taskID)
128
-
// TODO: Implement task completion
129
-
return nil
130
-
},
131
-
})
132
-
133
-
movieCmd := &cobra.Command{
134
-
Use: "movie",
135
-
Short: "Manage movie watch queue",
136
-
}
137
-
138
-
movieCmd.AddCommand(&cobra.Command{
139
-
Use: "add [title]",
140
-
Short: "Add movie to watch queue",
141
-
Args: cobra.MinimumNArgs(1),
142
-
RunE: func(cmd *cobra.Command, args []string) error {
143
-
title := args[0]
144
-
fmt.Printf("Adding movie: %s\n", title)
145
-
// TODO: Implement movie addition
146
-
return nil
147
-
},
148
-
})
149
-
150
-
movieCmd.AddCommand(&cobra.Command{
151
-
Use: "list",
152
-
Short: "List movies in queue",
153
-
RunE: func(cmd *cobra.Command, args []string) error {
154
-
fmt.Println("Listing movies...")
155
-
// TODO: Implement movie listing
156
-
return nil
157
-
},
158
-
})
159
-
160
-
rootCmd.AddCommand(movieCmd)
161
-
162
-
tvCmd := &cobra.Command{
163
-
Use: "tv",
164
-
Short: "Manage TV show watch queue",
165
}
166
167
-
tvCmd.AddCommand(&cobra.Command{
168
-
Use: "add [title]",
169
-
Short: "Add TV show to watch queue",
170
-
Args: cobra.MinimumNArgs(1),
171
-
RunE: func(cmd *cobra.Command, args []string) error {
172
-
title := args[0]
173
-
fmt.Printf("Adding TV show: %s\n", title)
174
-
// TODO: Implement TV show addition
175
-
return nil
176
-
},
177
-
})
178
-
179
-
tvCmd.AddCommand(&cobra.Command{
180
-
Use: "list",
181
-
Short: "List TV shows in queue",
182
-
RunE: func(cmd *cobra.Command, args []string) error {
183
-
fmt.Println("Listing TV shows...")
184
-
// TODO: Implement TV show listing
185
-
return nil
186
-
},
187
-
})
188
-
189
-
rootCmd.AddCommand(tvCmd)
190
-
191
-
bookCmd := &cobra.Command{
192
-
Use: "book",
193
-
Short: "Manage reading list",
194
}
195
196
-
bookCmd.AddCommand(&cobra.Command{
197
-
Use: "add [title]",
198
-
Short: "Add book to reading list",
199
-
Args: cobra.MinimumNArgs(1),
200
-
RunE: func(cmd *cobra.Command, args []string) error {
201
-
title := args[0]
202
-
fmt.Printf("Adding book: %s\n", title)
203
-
// TODO: Implement book addition
204
-
return nil
205
-
},
206
-
})
207
-
208
-
bookCmd.AddCommand(&cobra.Command{
209
-
Use: "list",
210
-
Short: "List books in reading list",
211
-
RunE: func(cmd *cobra.Command, args []string) error {
212
-
fmt.Println("Listing books...")
213
-
// TODO: Implement book listing
214
-
return nil
215
-
},
216
-
})
217
-
218
-
rootCmd.AddCommand(bookCmd)
219
-
220
-
noteCmd := &cobra.Command{
221
-
Use: "note",
222
-
Short: "Manage notes",
223
}
224
225
-
noteCmd.AddCommand(&cobra.Command{
226
-
Use: "create [title] [content...]",
227
-
Short: "Create a new note",
228
-
Aliases: []string{"new"},
229
-
RunE: func(cmd *cobra.Command, args []string) error {
230
-
return handlers.Create(cmd.Context(), args)
231
-
},
232
-
})
233
-
234
-
rootCmd.AddCommand(noteCmd)
235
-
236
-
rootCmd.AddCommand(&cobra.Command{
237
-
Use: "config [key] [value]",
238
-
Short: "Manage configuration",
239
-
Args: cobra.ExactArgs(2),
240
-
RunE: func(cmd *cobra.Command, args []string) error {
241
-
key, value := args[0], args[1]
242
-
fmt.Printf("Setting config %s = %s\n", key, value)
243
-
// TODO: Implement config management
244
-
return nil
245
-
},
246
-
})
247
-
248
-
if err := fang.Execute(context.Background(), rootCmd, fang.WithVersion("0.1.0")); err != nil {
249
os.Exit(1)
250
}
251
}
···
4
"context"
5
"fmt"
6
"os"
0
7
8
"github.com/charmbracelet/fang"
9
"github.com/spf13/cobra"
0
10
"github.com/stormlightlabs/noteleaf/internal/store"
11
"github.com/stormlightlabs/noteleaf/internal/ui"
12
"github.com/stormlightlabs/noteleaf/internal/utils"
···
25
return nil, fmt.Errorf("failed to initialize database: %w", err)
26
}
27
28
+
if config, err := store.LoadConfig(); err != nil {
0
29
return nil, fmt.Errorf("failed to load configuration: %w", err)
30
+
} else {
31
+
return &App{db: db, config: config}, nil
32
}
0
0
0
0
0
33
}
34
35
// Close cleans up application resources
···
50
}
51
defer app.Close()
52
53
+
root := rootCmd()
54
+
commands := []func() *cobra.Command{
55
+
setupCmd, resetCmd, statusCmd, todoCmd,
56
+
movieCmd, noteCmd, tvCmd, bookCmd, confCmd,
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
57
}
58
59
+
for _, cmdFunc := range commands {
60
+
cmd := cmdFunc()
61
+
root.AddCommand(cmd)
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
62
}
63
64
+
options := []fang.Option{
65
+
fang.WithVersion("0.1.0"),
66
+
fang.WithoutCompletions(),
67
+
fang.WithColorSchemeFunc(ui.NoteleafColorScheme),
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
68
}
69
70
+
if err := fang.Execute(context.Background(), root, options...); err != nil {
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
71
os.Exit(1)
72
}
73
}
+1
-1
codecov.yml
···
20
- "**/*_test.go"
21
- "**/testdata/**"
22
- "**/vendor/**"
23
-
- "cmd/cli/main.go" # Main entry point typically has minimal logic to test
···
20
- "**/*_test.go"
21
- "**/testdata/**"
22
- "**/vendor/**"
23
+
- "cmd/cli/*.go"
+6
-9
internal/ui/logo.go
···
12
type Logo int
13
14
const (
15
-
Collosal Logo = iota
16
Georgia
17
Alligator
18
ANSI
19
ANSIShadow
20
)
21
22
-
const collosal string = `
23
888b 888 888 888 .d888
24
8888b 888 888 888 d88P"
25
88888b 888 888 888 888
···
62
63
func (l Logo) String() string {
64
switch l {
65
-
case Collosal:
66
-
return collosal
67
case Georgia:
68
return georgia
69
case Alligator:
···
73
case ANSIShadow:
74
return ansiShadow
75
default:
76
-
return collosal
77
}
78
}
79
···
130
vp := viewport.New(maxWidth+4, len(lines))
131
vp.SetContent(coloredLogo)
132
133
-
style := lipgloss.NewStyle().
134
-
Border(lipgloss.RoundedBorder()).
135
-
BorderForeground(lipgloss.Color("#6b7280")).
136
-
Padding(1)
137
138
if len(renderer) > 0 && renderer[0] != nil {
139
style = style.Renderer(renderer[0])
···
12
type Logo int
13
14
const (
15
+
Colossal Logo = iota
16
Georgia
17
Alligator
18
ANSI
19
ANSIShadow
20
)
21
22
+
const colossal string = `
23
888b 888 888 888 .d888
24
8888b 888 888 888 d88P"
25
88888b 888 888 888 888
···
62
63
func (l Logo) String() string {
64
switch l {
65
+
case Colossal:
66
+
return colossal
67
case Georgia:
68
return georgia
69
case Alligator:
···
73
case ANSIShadow:
74
return ansiShadow
75
default:
76
+
return colossal
77
}
78
}
79
···
130
vp := viewport.New(maxWidth+4, len(lines))
131
vp.SetContent(coloredLogo)
132
133
+
style := lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color("#6b7280")).PaddingLeft(1)
0
0
0
134
135
if len(renderer) > 0 && renderer[0] != nil {
136
style = style.Renderer(renderer[0])
+2
-2
internal/ui/ui_test.go
···
270
name string
271
contains string
272
}{
273
-
{Collosal, "Collosal", "888b 888"},
274
{Georgia, "Georgia", "`7MN. `7MF'"},
275
{Alligator, "Alligator", ":::: ::: ::::::::"},
276
{ANSI, "ANSI", "โโโ โโ"},
···
308
309
t.Run("Colored in Viewport", func(t *testing.T) {
310
withColor(t, func(r *lipgloss.Renderer) {
311
-
logo := Collosal
312
viewport := logo.ColoredInViewport(r)
313
t.Logf("viewport output:\n%s", viewport)
314
···
270
name string
271
contains string
272
}{
273
+
{Colossal, "Collosal", "888b 888"},
274
{Georgia, "Georgia", "`7MN. `7MF'"},
275
{Alligator, "Alligator", ":::: ::: ::::::::"},
276
{ANSI, "ANSI", "โโโ โโ"},
···
308
309
t.Run("Colored in Viewport", func(t *testing.T) {
310
withColor(t, func(r *lipgloss.Renderer) {
311
+
logo := Colossal
312
viewport := logo.ColoredInViewport(r)
313
t.Logf("viewport output:\n%s", viewport)
314