startmeow 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/StartMeow +0 -0
- package/debug.log +3 -0
- package/go.mod +28 -0
- package/go.sum +44 -0
- package/internal/README.md +1 -0
- package/internal/generator.go +99 -0
- package/internal/projbuilder.go +188 -0
- package/internal/prompts.go +138 -0
- package/internal/queue.go +57 -0
- package/internal/states.go +88 -0
- package/internal/tui/debug.log +5 -0
- package/internal/tui/go.sum +44 -0
- package/internal/tui/models/app.go +260 -0
- package/internal/tui/models/help.go +54 -0
- package/internal/tui/models/question.go +34 -0
- package/internal/tui/styles/style.go +82 -0
- package/internal/types.go +78 -0
- package/internal/userinterface_string.go +26 -0
- package/main.go +60 -0
- package/manifest.json +5 -0
- package/manifests/manifest.json +7 -0
- package/package.json +28 -0
- package/templates/backend/README.md +1 -0
- package/templates/backend/express/README.md.tmpl +9 -0
- package/templates/backend/express/index.js.tmpl +13 -0
- package/templates/backend/express/package.json.tmpl +11 -0
- package/templates/frontend/README.md +1 -0
- package/templates/ui/README.md +1 -0
- package/templates/ui/blog/home.tsx.tmpl +53 -0
- package/templates/ui/blog/index.html +48 -0
- package/templates/ui/blog/page.tsx +45 -0
- package/templates/ui/blog/page.tsx.tmpl +45 -0
- package/templates/ui/download/home.tsx.tmpl +53 -0
- package/templates/ui/download/index.html +51 -0
- package/templates/ui/download/manifest.json +5 -0
- package/templates/ui/download/page.tsx +49 -0
- package/templates/ui/download/page.tsx.tmpl +49 -0
- package/templates/ui/landing/home.tsx.tmpl +55 -0
- package/templates/ui/landing/index.html +53 -0
- package/templates/ui/landing/page.tsx +51 -0
- package/templates/ui/landing/page.tsx.tmpl +51 -0
- package/templates/ui/store/home.tsx.tmpl +49 -0
- package/templates/ui/store/index.html +47 -0
- package/templates/ui/store/page.tsx +45 -0
- package/templates/ui/store/page.tsx.tmpl +45 -0
- package/test-project/README.md +36 -0
- package/test-project/app/favicon.ico +0 -0
- package/test-project/app/globals.css +26 -0
- package/test-project/app/layout.tsx +34 -0
- package/test-project/app/page.tsx +49 -0
- package/test-project/eslint.config.mjs +18 -0
- package/test-project/next.config.ts +7 -0
- package/test-project/package-lock.json +6593 -0
- package/test-project/package.json +26 -0
- package/test-project/postcss.config.mjs +7 -0
- package/test-project/public/file.svg +1 -0
- package/test-project/public/globe.svg +1 -0
- package/test-project/public/next.svg +1 -0
- package/test-project/public/vercel.svg +1 -0
- package/test-project/public/window.svg +1 -0
- package/test-project/tsconfig.json +34 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
charm.land/bubbles/v2 v2.0.0 h1:tE3eK/pHjmtrDiRdoC9uGNLgpopOd8fjhEe31B/ai5s=
|
|
2
|
+
charm.land/bubbles/v2 v2.0.0/go.mod h1:rCHoleP2XhU8um45NTuOWBPNVHxnkXKTiZqcclL/qOI=
|
|
3
|
+
charm.land/bubbletea/v2 v2.0.2 h1:4CRtRnuZOdFDTWSff9r8QFt/9+z6Emubz3aDMnf/dx0=
|
|
4
|
+
charm.land/bubbletea/v2 v2.0.2/go.mod h1:3LRff2U4WIYXy7MTxfbAQ+AdfM3D8Xuvz2wbsOD9OHQ=
|
|
5
|
+
charm.land/lipgloss/v2 v2.0.0 h1:sd8N/B3x892oiOjFfBQdXBQp3cAkvjGaU5TvVZC3ivo=
|
|
6
|
+
charm.land/lipgloss/v2 v2.0.0/go.mod h1:w6SnmsBFBmEFBodiEDurGS/sdUY/u1+v72DqUzc6J14=
|
|
7
|
+
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
|
8
|
+
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
|
9
|
+
github.com/aymanbagabas/go-udiff v0.4.0 h1:TKnLPh7IbnizJIBKFWa9mKayRUBQ9Kh1BPCk6w2PnYM=
|
|
10
|
+
github.com/aymanbagabas/go-udiff v0.4.0/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
|
11
|
+
github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY=
|
|
12
|
+
github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8=
|
|
13
|
+
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 h1:eyFRbAmexyt43hVfeyBofiGSEmJ7krjLOYt/9CF5NKA=
|
|
14
|
+
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8/go.mod h1:SQpCTRNBtzJkwku5ye4S3HEuthAlGy2n9VXZnWkEW98=
|
|
15
|
+
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
|
|
16
|
+
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
|
17
|
+
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
|
18
|
+
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
|
19
|
+
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
|
20
|
+
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
|
21
|
+
github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY=
|
|
22
|
+
github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo=
|
|
23
|
+
github.com/charmbracelet/x/windows v0.2.2 h1:IofanmuvaxnKHuV04sC0eBy/smG6kIKrWG2/jYn2GuM=
|
|
24
|
+
github.com/charmbracelet/x/windows v0.2.2/go.mod h1:/8XtdKZzedat74NQFn0NGlGL4soHB0YQZrETF96h75k=
|
|
25
|
+
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
|
26
|
+
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
|
27
|
+
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
|
28
|
+
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
|
29
|
+
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
|
30
|
+
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
|
31
|
+
github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ=
|
|
32
|
+
github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
|
33
|
+
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
|
34
|
+
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
|
35
|
+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
|
36
|
+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
|
37
|
+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
|
38
|
+
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
|
39
|
+
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
|
40
|
+
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
|
|
41
|
+
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
|
42
|
+
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
|
43
|
+
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
|
44
|
+
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
package internal
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"StartMeow/internal"
|
|
5
|
+
style "StartMeow/internal/tui/styles"
|
|
6
|
+
"fmt"
|
|
7
|
+
"strings"
|
|
8
|
+
|
|
9
|
+
"charm.land/bubbles/v2/help"
|
|
10
|
+
"charm.land/bubbles/v2/textinput"
|
|
11
|
+
"charm.land/lipgloss/v2"
|
|
12
|
+
|
|
13
|
+
tea "charm.land/bubbletea/v2"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
type model struct {
|
|
17
|
+
styles *style.MainStyle
|
|
18
|
+
width int
|
|
19
|
+
height int
|
|
20
|
+
|
|
21
|
+
questions []Question // list of questions
|
|
22
|
+
project internal.Project
|
|
23
|
+
qIndex int
|
|
24
|
+
answerField textinput.Model
|
|
25
|
+
help help.Model
|
|
26
|
+
keys help.KeyMap
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func (m model) Init() tea.Cmd {
|
|
30
|
+
return nil
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
34
|
+
var cmd tea.Cmd
|
|
35
|
+
currentQ := &m.questions[m.qIndex] // get current question
|
|
36
|
+
|
|
37
|
+
switch msg := msg.(type) {
|
|
38
|
+
case tea.WindowSizeMsg:
|
|
39
|
+
m.width = msg.Width
|
|
40
|
+
m.height = msg.Height
|
|
41
|
+
|
|
42
|
+
case tea.KeyPressMsg:
|
|
43
|
+
switch msg.String() {
|
|
44
|
+
case "?":
|
|
45
|
+
m.help.ShowAll = !m.help.ShowAll
|
|
46
|
+
case "ctrl+c":
|
|
47
|
+
return m, tea.Quit
|
|
48
|
+
|
|
49
|
+
case "enter":
|
|
50
|
+
// if we are at the end of the form, do not go to next
|
|
51
|
+
if currentQ.Prompt.PromptType == internal.Info {
|
|
52
|
+
return m, tea.Quit
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
m.NextQuestion()
|
|
56
|
+
currentQ.Prompt.Input = m.answerField.Value()
|
|
57
|
+
m.SetAnswerValue()
|
|
58
|
+
return m, nil
|
|
59
|
+
|
|
60
|
+
case "up":
|
|
61
|
+
// if select form, then move option index
|
|
62
|
+
if currentQ.Prompt.PromptType == internal.Select {
|
|
63
|
+
currentQ.PrevOption()
|
|
64
|
+
return m, nil
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
case "down":
|
|
68
|
+
// if select form, then move option index
|
|
69
|
+
if currentQ.Prompt.PromptType == internal.Select {
|
|
70
|
+
currentQ.NextOption()
|
|
71
|
+
return m, nil
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
case "y":
|
|
75
|
+
// if we are at the end of the form, make Y a command
|
|
76
|
+
if currentQ.Prompt.PromptType == internal.Info {
|
|
77
|
+
return m, tea.Quit
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
case "n":
|
|
81
|
+
// if we are at the end of the form, make N a command
|
|
82
|
+
if currentQ.Prompt.PromptType == internal.Info {
|
|
83
|
+
m.ClearAnswers()
|
|
84
|
+
m.NextQuestion()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
m.answerField, cmd = m.answerField.Update(msg)
|
|
89
|
+
return m, cmd
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
func (m model) View() tea.View {
|
|
93
|
+
var tabView string
|
|
94
|
+
|
|
95
|
+
currentQuestion := m.questions[m.qIndex]
|
|
96
|
+
|
|
97
|
+
switch currentQuestion.Prompt.PromptType {
|
|
98
|
+
// current question is prompt
|
|
99
|
+
case internal.Field:
|
|
100
|
+
tabView = m.ViewTab(
|
|
101
|
+
lipgloss.JoinVertical(
|
|
102
|
+
lipgloss.Left,
|
|
103
|
+
m.styles.TabStyle.Render(
|
|
104
|
+
m.ViewField(),
|
|
105
|
+
),
|
|
106
|
+
))
|
|
107
|
+
|
|
108
|
+
// current question is select
|
|
109
|
+
case internal.Select:
|
|
110
|
+
// log.Println("Case: Select")
|
|
111
|
+
tabView = m.ViewTab(
|
|
112
|
+
lipgloss.JoinVertical(
|
|
113
|
+
lipgloss.Left,
|
|
114
|
+
m.styles.TabStyle.Render(
|
|
115
|
+
m.ViewSelect(),
|
|
116
|
+
),
|
|
117
|
+
),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
case internal.Info:
|
|
121
|
+
// current question is verify
|
|
122
|
+
tabView = m.ViewTab(
|
|
123
|
+
lipgloss.JoinVertical(
|
|
124
|
+
lipgloss.Left,
|
|
125
|
+
m.styles.TabStyle.Render(
|
|
126
|
+
m.ViewVerify(),
|
|
127
|
+
),
|
|
128
|
+
))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
helpView := lipgloss.Place(
|
|
132
|
+
m.width,
|
|
133
|
+
2,
|
|
134
|
+
lipgloss.Center,
|
|
135
|
+
lipgloss.Bottom,
|
|
136
|
+
m.styles.Title.Render(m.help.View(m.keys)),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
finalView := lipgloss.JoinVertical(lipgloss.Center, tabView, helpView)
|
|
140
|
+
|
|
141
|
+
v := tea.NewView(finalView)
|
|
142
|
+
v.BackgroundColor = lipgloss.Color(style.BackgroundBlack)
|
|
143
|
+
v.AltScreen = true
|
|
144
|
+
return v
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
func (m model) ViewTab(view string) string {
|
|
148
|
+
return lipgloss.Place(
|
|
149
|
+
m.width,
|
|
150
|
+
m.height/2,
|
|
151
|
+
lipgloss.Center,
|
|
152
|
+
lipgloss.Bottom,
|
|
153
|
+
view,
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
func (m model) ViewField() string {
|
|
158
|
+
var rows []string
|
|
159
|
+
|
|
160
|
+
rows = append(rows, m.styles.Title.Render(m.questions[m.qIndex].Prompt.Question))
|
|
161
|
+
rows = append(rows, m.styles.InputField.Render(m.answerField.View()))
|
|
162
|
+
|
|
163
|
+
return strings.Join(rows, "\n")
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
func (m model) ViewSelect() string {
|
|
167
|
+
var rows []string
|
|
168
|
+
|
|
169
|
+
rows = append(rows, m.styles.Title.Render(m.questions[m.qIndex].Prompt.Question))
|
|
170
|
+
|
|
171
|
+
currentQuestion := m.questions[m.qIndex]
|
|
172
|
+
options := currentQuestion.Prompt.Options
|
|
173
|
+
optionIndex := currentQuestion.OptionIndex
|
|
174
|
+
|
|
175
|
+
for i, v := range options {
|
|
176
|
+
if optionIndex == i {
|
|
177
|
+
rows = append(rows, m.styles.OptionSelect.Render(fmt.Sprintf("[x] %s", v)))
|
|
178
|
+
} else {
|
|
179
|
+
rows = append(rows, m.styles.Options.Render(fmt.Sprintf("[ ] %s", v)))
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return strings.Join(rows, "\n")
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
func (m model) ViewVerify() string {
|
|
187
|
+
var rows []string
|
|
188
|
+
|
|
189
|
+
rows = append(rows, m.styles.Title.Render(m.questions[m.qIndex].Prompt.Question))
|
|
190
|
+
// rows = append(rows, "")
|
|
191
|
+
|
|
192
|
+
for _, v := range m.questions {
|
|
193
|
+
var s string
|
|
194
|
+
switch v.Prompt.PromptType {
|
|
195
|
+
case internal.Select:
|
|
196
|
+
s = fmt.Sprintf("%s: %s", m.styles.Options.Render(v.Prompt.Question), m.styles.OptionSelect.Render(v.Prompt.Options[v.OptionIndex]))
|
|
197
|
+
case internal.Field:
|
|
198
|
+
s = fmt.Sprintf("%s: %s", m.styles.Options.Render(v.Prompt.Question), m.styles.OptionSelect.Render(v.Prompt.Input))
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if s != "" {
|
|
202
|
+
rows = append(rows, s)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
s := m.styles.OptionSelect.Render("\nConfirm project? (Y/n):")
|
|
207
|
+
rows = append(rows, s)
|
|
208
|
+
|
|
209
|
+
return strings.Join(rows, "\n")
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
func (m *model) NextQuestion() {
|
|
213
|
+
if m.qIndex < len(m.questions)-1 {
|
|
214
|
+
m.qIndex++
|
|
215
|
+
} else {
|
|
216
|
+
m.qIndex = 0
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
func (m *model) PrevQuestion() {
|
|
221
|
+
if m.qIndex > 0 {
|
|
222
|
+
m.qIndex--
|
|
223
|
+
} else {
|
|
224
|
+
m.qIndex = len(m.questions) - 1
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
func (m *model) SetAnswerValue() {
|
|
229
|
+
currentAnswer := m.questions[m.qIndex].Prompt.Input
|
|
230
|
+
|
|
231
|
+
if currentAnswer != "" {
|
|
232
|
+
m.answerField.SetValue(currentAnswer)
|
|
233
|
+
} else {
|
|
234
|
+
m.answerField.SetValue("")
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
func (m *model) ClearAnswers() {
|
|
239
|
+
q := m.questions
|
|
240
|
+
|
|
241
|
+
for i := range q {
|
|
242
|
+
q[i].Prompt.Input = ""
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
func NewDefaultModel(questions []Question) *model {
|
|
247
|
+
mainStyle := style.DefaultStyles()
|
|
248
|
+
|
|
249
|
+
answerField := textinput.New()
|
|
250
|
+
answerField.Focus()
|
|
251
|
+
|
|
252
|
+
return &model{
|
|
253
|
+
qIndex: 0,
|
|
254
|
+
questions: questions,
|
|
255
|
+
answerField: answerField,
|
|
256
|
+
styles: mainStyle,
|
|
257
|
+
keys: keys,
|
|
258
|
+
help: help.New(),
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
package internal
|
|
2
|
+
|
|
3
|
+
import "charm.land/bubbles/v2/key"
|
|
4
|
+
|
|
5
|
+
type keyMap struct {
|
|
6
|
+
Up key.Binding
|
|
7
|
+
Down key.Binding
|
|
8
|
+
Left key.Binding
|
|
9
|
+
Right key.Binding
|
|
10
|
+
Help key.Binding
|
|
11
|
+
Quit key.Binding
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
var keys = keyMap{
|
|
15
|
+
Up: key.NewBinding(
|
|
16
|
+
key.WithKeys("up"),
|
|
17
|
+
key.WithHelp("↑", "move up"),
|
|
18
|
+
),
|
|
19
|
+
Down: key.NewBinding(
|
|
20
|
+
key.WithKeys("down"),
|
|
21
|
+
key.WithHelp("↓", "move down"),
|
|
22
|
+
),
|
|
23
|
+
// Left: key.NewBinding(
|
|
24
|
+
// key.WithKeys("left"),
|
|
25
|
+
// key.WithHelp("←", "move left"),
|
|
26
|
+
// ),
|
|
27
|
+
// Right: key.NewBinding(
|
|
28
|
+
// key.WithKeys("right"),
|
|
29
|
+
// key.WithHelp("→", "move right"),
|
|
30
|
+
// ),
|
|
31
|
+
Help: key.NewBinding(
|
|
32
|
+
key.WithKeys("?"),
|
|
33
|
+
key.WithHelp("?", "toggle help"),
|
|
34
|
+
),
|
|
35
|
+
Quit: key.NewBinding(
|
|
36
|
+
key.WithKeys("ctrl+c"),
|
|
37
|
+
key.WithHelp("ctrl+c", "quit"),
|
|
38
|
+
),
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ShortHelp returns keybindings to be shown in the mini help view. It's part
|
|
42
|
+
// of the key.Map interface.
|
|
43
|
+
func (k keyMap) ShortHelp() []key.Binding {
|
|
44
|
+
return []key.Binding{k.Help, k.Quit}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// FullHelp returns keybindings for the expanded help view. It's part of the
|
|
48
|
+
// key.Map interface.
|
|
49
|
+
func (k keyMap) FullHelp() [][]key.Binding {
|
|
50
|
+
return [][]key.Binding{
|
|
51
|
+
{k.Up, k.Down}, // first column
|
|
52
|
+
{k.Help, k.Quit}, // second column
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
package internal
|
|
2
|
+
|
|
3
|
+
import internal "StartMeow/internal"
|
|
4
|
+
|
|
5
|
+
type Question struct {
|
|
6
|
+
Prompt internal.Prompt
|
|
7
|
+
OptionIndex int
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
func (q *Question) NextOption() {
|
|
11
|
+
if q.OptionIndex < len(q.Prompt.Options)-1 {
|
|
12
|
+
q.OptionIndex++
|
|
13
|
+
} else {
|
|
14
|
+
q.OptionIndex = 0
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
func (q *Question) PrevOption() {
|
|
19
|
+
if q.OptionIndex > 0 {
|
|
20
|
+
q.OptionIndex--
|
|
21
|
+
} else {
|
|
22
|
+
q.OptionIndex = len(q.Prompt.Options) - 1
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
func NewQuestion(question string, qtype internal.PromptType, options []string) Question {
|
|
27
|
+
p := internal.Prompt{
|
|
28
|
+
Question: question,
|
|
29
|
+
PromptType: qtype,
|
|
30
|
+
Options: options,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return Question{Prompt: p, OptionIndex: 0}
|
|
34
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
package internal
|
|
2
|
+
|
|
3
|
+
import "charm.land/lipgloss/v2"
|
|
4
|
+
|
|
5
|
+
const (
|
|
6
|
+
White = "#f8edeb"
|
|
7
|
+
LightOrange = "#f9dcc4"
|
|
8
|
+
Orange = "#fec89a"
|
|
9
|
+
Black = "#d6d0c8"
|
|
10
|
+
BackgroundBlack = "#3A2E2E"
|
|
11
|
+
Black2 = "#dcd2cc"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
type MainStyle struct {
|
|
15
|
+
Title lipgloss.Style
|
|
16
|
+
Options lipgloss.Style
|
|
17
|
+
OptionSelect lipgloss.Style
|
|
18
|
+
InputField lipgloss.Style
|
|
19
|
+
TabStyle lipgloss.Style
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
func DefaultStyles() *MainStyle {
|
|
23
|
+
m := new(MainStyle)
|
|
24
|
+
|
|
25
|
+
m.Title = TitleStyle()
|
|
26
|
+
m.Options = OptionsStyle()
|
|
27
|
+
m.OptionSelect = OptionSelectStyle()
|
|
28
|
+
m.InputField = InputFieldStyle()
|
|
29
|
+
m.TabStyle = TabStyle()
|
|
30
|
+
|
|
31
|
+
return m
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
func InputFieldStyle() lipgloss.Style {
|
|
35
|
+
s := lipgloss.NewStyle().
|
|
36
|
+
BorderForeground(
|
|
37
|
+
lipgloss.Color(Orange),
|
|
38
|
+
).
|
|
39
|
+
BorderStyle(lipgloss.NormalBorder()).
|
|
40
|
+
Padding(1).
|
|
41
|
+
Width(40)
|
|
42
|
+
|
|
43
|
+
return s
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func TabStyle() lipgloss.Style {
|
|
47
|
+
s := lipgloss.NewStyle().
|
|
48
|
+
BorderForeground(
|
|
49
|
+
lipgloss.Color(Black),
|
|
50
|
+
).
|
|
51
|
+
BorderStyle(lipgloss.NormalBorder()).
|
|
52
|
+
Padding(1, 3)
|
|
53
|
+
|
|
54
|
+
return s
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
func TitleStyle() lipgloss.Style {
|
|
58
|
+
s := lipgloss.NewStyle().
|
|
59
|
+
Foreground(
|
|
60
|
+
lipgloss.Color(Orange),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return s
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func OptionsStyle() lipgloss.Style {
|
|
67
|
+
s := lipgloss.NewStyle().
|
|
68
|
+
Foreground(
|
|
69
|
+
lipgloss.Color(White),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return s
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
func OptionSelectStyle() lipgloss.Style {
|
|
76
|
+
s := lipgloss.NewStyle().
|
|
77
|
+
Foreground(
|
|
78
|
+
lipgloss.Color(LightOrange),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return s
|
|
82
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
package internal
|
|
2
|
+
|
|
3
|
+
//go:generate stringer -type=UserInterface
|
|
4
|
+
|
|
5
|
+
type UserInterface int
|
|
6
|
+
|
|
7
|
+
const (
|
|
8
|
+
Store UserInterface = iota
|
|
9
|
+
Download
|
|
10
|
+
Blog
|
|
11
|
+
Landing
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
type ProjectType int
|
|
15
|
+
type Framework int
|
|
16
|
+
type Language int
|
|
17
|
+
type PromptType int
|
|
18
|
+
type Database int
|
|
19
|
+
|
|
20
|
+
const (
|
|
21
|
+
WebApp ProjectType = iota
|
|
22
|
+
ClientServer
|
|
23
|
+
Mobile
|
|
24
|
+
Terminal
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const (
|
|
28
|
+
React Framework = iota
|
|
29
|
+
ExpressJs
|
|
30
|
+
NodeJS
|
|
31
|
+
NextJS
|
|
32
|
+
ReactRouter
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
const (
|
|
36
|
+
C Language = iota
|
|
37
|
+
Javascript
|
|
38
|
+
Typescript
|
|
39
|
+
Go
|
|
40
|
+
CPlusPlus
|
|
41
|
+
Java
|
|
42
|
+
Swift
|
|
43
|
+
Kotlin
|
|
44
|
+
CSharp
|
|
45
|
+
Jsx
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
const (
|
|
49
|
+
Select PromptType = iota
|
|
50
|
+
Field
|
|
51
|
+
Info
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
const (
|
|
55
|
+
MongoDB Database = iota
|
|
56
|
+
SQLite
|
|
57
|
+
None
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
type Project struct {
|
|
61
|
+
ProjType ProjectType
|
|
62
|
+
FrontFrame Framework
|
|
63
|
+
BackFrame Framework
|
|
64
|
+
FrontLang Language
|
|
65
|
+
BackLang Language
|
|
66
|
+
Ui UserInterface
|
|
67
|
+
Database Database
|
|
68
|
+
ProjName string
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
type Prompt struct {
|
|
72
|
+
Question string
|
|
73
|
+
PromptType PromptType
|
|
74
|
+
Options []string
|
|
75
|
+
Input string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
var templatePaths = map[string]string{"expressServer": "src/server.js"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Code generated by "stringer -type=UserInterface"; DO NOT EDIT.
|
|
2
|
+
|
|
3
|
+
package internal
|
|
4
|
+
|
|
5
|
+
import "strconv"
|
|
6
|
+
|
|
7
|
+
func _() {
|
|
8
|
+
// An "invalid array index" compiler error signifies that the constant values have changed.
|
|
9
|
+
// Re-run the stringer command to generate them again.
|
|
10
|
+
var x [1]struct{}
|
|
11
|
+
_ = x[Store-0]
|
|
12
|
+
_ = x[Download-1]
|
|
13
|
+
_ = x[Blog-2]
|
|
14
|
+
_ = x[Landing-3]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const _UserInterface_name = "StoreDownloadBlogEmpty"
|
|
18
|
+
|
|
19
|
+
var _UserInterface_index = [...]uint8{0, 5, 13, 17, 22}
|
|
20
|
+
|
|
21
|
+
func (i UserInterface) String() string {
|
|
22
|
+
if i < 0 || i >= UserInterface(len(_UserInterface_index)-1) {
|
|
23
|
+
return "UserInterface(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
24
|
+
}
|
|
25
|
+
return _UserInterface_name[_UserInterface_index[i]:_UserInterface_index[i+1]]
|
|
26
|
+
}
|
package/main.go
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
internal "StartMeow/internal"
|
|
5
|
+
models "StartMeow/internal/tui/models"
|
|
6
|
+
"fmt"
|
|
7
|
+
"log"
|
|
8
|
+
"os"
|
|
9
|
+
|
|
10
|
+
tea "charm.land/bubbletea/v2"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
func main() {
|
|
14
|
+
f, err := tea.LogToFile("debug.log", "debug")
|
|
15
|
+
if err != nil {
|
|
16
|
+
log.Fatal(err)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
defer f.Close()
|
|
20
|
+
|
|
21
|
+
questions := []models.Question{
|
|
22
|
+
models.NewQuestion("Select a language", internal.Select, []string{"C", "C#", "Java", "JavaScript"}),
|
|
23
|
+
models.NewQuestion("Insert project name", internal.Field, []string{}),
|
|
24
|
+
models.NewQuestion("Verify project structure", internal.Info, []string{}),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
m := models.NewDefaultModel(questions)
|
|
28
|
+
|
|
29
|
+
p := tea.NewProgram(m)
|
|
30
|
+
if _, err := p.Run(); err != nil {
|
|
31
|
+
fmt.Printf("Error occurred: %v", err)
|
|
32
|
+
os.Exit(1)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// testProj := internal.Project{
|
|
36
|
+
// ProjType: internal.WebApp,
|
|
37
|
+
// FrontFrame: internal.React,
|
|
38
|
+
// BackFrame: internal.NextJS,
|
|
39
|
+
// FrontLang: internal.Javascript,
|
|
40
|
+
// BackLang: internal.Typescript,
|
|
41
|
+
// Ui: internal.Download,
|
|
42
|
+
// Database: internal.MongoDB,
|
|
43
|
+
// ProjName: "test-project",
|
|
44
|
+
// }
|
|
45
|
+
|
|
46
|
+
// internal.BuildProject(&testProj)
|
|
47
|
+
|
|
48
|
+
// type Project struct {
|
|
49
|
+
// ProjType ProjectType
|
|
50
|
+
// FrontFrame Framework
|
|
51
|
+
// BackFrame Framework
|
|
52
|
+
// FrontLang Language
|
|
53
|
+
// BackLang Language
|
|
54
|
+
// Ui UserInterface
|
|
55
|
+
// Database Database
|
|
56
|
+
// ProjName string
|
|
57
|
+
// FilePath string
|
|
58
|
+
// }
|
|
59
|
+
|
|
60
|
+
}
|
package/manifest.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "startmeow",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI Tool for base project generation.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"CLI",
|
|
7
|
+
"Project",
|
|
8
|
+
"Project",
|
|
9
|
+
"Generation",
|
|
10
|
+
"Baseline"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/lucaslav05/StartMeow#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/lucaslav05/StartMeow/issues"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/lucaslav05/StartMeow.git"
|
|
19
|
+
},
|
|
20
|
+
"license": "ISC",
|
|
21
|
+
"author": "Lucas",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "main.go",
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "go build",
|
|
26
|
+
"test": "go run main.go"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Backend Templates
|