From: christiangoeschel Date: Tue, 1 Oct 2024 20:16:55 +0000 (-0400) Subject: Restructuring of module directory trees and source code refactoring X-Git-Url: https://git.christiangoeschel.com/?a=commitdiff_plain;h=718713e256ffcf2c7d3c89616864c9b15b72b73a;p=repoman-cli.git Restructuring of module directory trees and source code refactoring --- diff --git a/cli/main b/cli/main deleted file mode 100755 index ccf0d0b..0000000 Binary files a/cli/main and /dev/null differ diff --git a/cli/main.go b/cli/main.go deleted file mode 100644 index 8469ae3..0000000 --- a/cli/main.go +++ /dev/null @@ -1,299 +0,0 @@ -package main - -// An example demonstrating an application with multiple views. -// -// Note that this example was produced before the Bubbles progress component -// was available (github.com/charmbracelet/bubbles/progress) and thus, we're -// implementing a progress bar from scratch here. - -import ( - "fmt" - "net/http" - "io/ioutil" - // "time" - "encoding/json" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -const ( - dotChar = " • " - arrowRightChar = " → " - apiUrl = "https://repoman.christiangoeschel.com:8443" -) - -// General stuff for styling the view -var ( - spinnerStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("69")) - keywordStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("211")) - subtleStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("241")) - checkboxStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#03A9F4")) - titleStyle = lipgloss.NewStyle().Background(lipgloss.Color("#448AFF")).Foreground(lipgloss.Color("#FFFFFF")) - statusOK = lipgloss.NewStyle().Foreground(lipgloss.Color("200")) - dotStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("236")).Render(dotChar) - mainStyle = lipgloss.NewStyle().Margin(10) - - - mainMenuOptions = []string{ - "List all repos", - "Create a new repository", - "Delete a repository", - "Manage API Keys", - "Check API Status", - "Settings", - "Quit", - } - - -) - -func main() { - initialModel := model{0, false, false, APIStatusMsg{} } - - p := tea.NewProgram(initialModel, tea.WithAltScreen()) - if _, err := p.Run(); err != nil { - fmt.Println("could not start program:", err) - } - -} - -type ( - frameMsg struct{} -) - - -type model struct { - Choice int - Chosen bool - Quitting bool - APIStatus APIStatusMsg -} - -func (m model) Init() tea.Cmd { - - return nil -} - -// Main update function. -func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - - // Make sure these keys always quit - if msg, ok := msg.(tea.KeyMsg); ok { - key := msg.String() - - if key == "q" || key == "esc" || key == "ctrl+c" { - m.Quitting = true - return m, tea.Quit - } else if key == "backspace" { - m.Chosen = false - } - } - - // Hand off the message and model to the appropriate update function for the - // appropriate view based on the current state. - if !m.Chosen { - return updateMainMenu(msg, m) - } - - return updateChosen(msg, m) -} - -// The main view, which just calls the appropriate sub-view -func (m model) View() string { - var s string - if m.Quitting { - return "\n See you soon my friend! \n\n" - } - if !m.Chosen { - s = mainMenuView(m) - } else { - s = chosenView(m) - } - - return mainStyle.Render("\n" + s + "\n\n") -} - -// Sub-update functions - -// Update loop for the first view where you're choosing a task. -func updateMainMenu(msg tea.Msg, m model) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.KeyMsg: - var lastChoice int = len(mainMenuOptions) - 1 - - switch msg.String() { - - case "j", "down": - m.Choice++ - if m.Choice > lastChoice { - m.Choice = 0 - } - case "k", "up": - m.Choice-- - if m.Choice < 0 { - m.Choice = lastChoice - } - case "enter", " ": - m.Chosen = true - - if mainMenuOptions[m.Choice] == "Quit" { - return m, tea.Quit - } - - return m, nil - } - -} - - return m, nil -} - - -// Update loop for the second view after a choice has been made -func updateChosen(msg tea.Msg, m model) (tea.Model, tea.Cmd) { - - switch msg := msg.(type) { - - case tea.KeyMsg: - switch msg.String() { - case "enter": - if m.Choice == 4 { - m.APIStatus = APIStatusMsg{} - return m, handleAPIStatusRequest(apiUrl) - } - } - - case APIStatusMsg: - m.APIStatus = msg - - return m, nil - } - - return m, nil -} - -// Sub-views - -// The first view, where you're choosing a task -func mainMenuView(m model) string { - c := m.Choice - - tpl := fmt.Sprintf("%s\n\n", titleStyle.Render("[ +-+ Repoman CLI +-+ ]")) - tpl += "Main Menu\n\n" - tpl += "%s\n\n" - tpl += fmt.Sprintf("%s\n%s\n%s\n%s\n", subtleStyle.Render("j/k, up/down: select"), subtleStyle.Render("enter, spacebar: choose"), - subtleStyle.Render("q, esc: quit"), subtleStyle.Render("backspace: main menu")) - - var choices string - for i := 0;i < len(mainMenuOptions);i++{ - choices += fmt.Sprintf("%s\n", checkbox(mainMenuOptions[i], c == i)) - } - - return fmt.Sprintf(tpl, choices) -} - -// The second view, after a task has been chosen -func chosenView(m model) string { - var msg string - - switch m.Choice { - - case 0: - msg = fmt.Sprintf("%s\n\nCool, we'll need %s and %s...", - titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), - keywordStyle.Render("libgarden"), - keywordStyle.Render("vegeutils")) - - case 1: - msg = fmt.Sprintf("%s\n\nOkay, then we should install %s and %s...", - titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), - keywordStyle.Render("marketkit"), - keywordStyle.Render("libshopping")) - - case 2: - msg = fmt.Sprintf("%s\n\nOkay, cool, then we’ll need a library. Yes, an %s.", - titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), - keywordStyle.Render("actual library")) - - case 3: - msg = fmt.Sprintf("%s\n\nOkay, cool, then we’ll need a library. Yes, an %s.", - titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), - keywordStyle.Render("actual library")) - - case 4: - - statusResponse := fmt.Sprintf("Status:%s\nDescription:%s\nStart:%s\nExpected End:%s\n", - keywordStyle.Render(m.APIStatus.Status), - keywordStyle.Render(m.APIStatus.Description), - keywordStyle.Render(m.APIStatus.StartDateTime), - keywordStyle.Render(m.APIStatus.EstimatedEndTime)) - - msg = fmt.Sprintf("%s\n\nRequesting API Server Status from:\n%s\n\nResponse:\n\n%v\n\n%s", - titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), - keywordStyle.Render(apiUrl), - statusResponse, - subtleStyle.Render("Press Enter to refresh")) - - case 5: - msg = fmt.Sprintf("%s\n\nOkay, cool, then we’ll need a library. Yes, an %s.", - titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), - keywordStyle.Render("actual library")) - - default: - msg = fmt.Sprintf("Seems like you are lost.\n\nI'll help you find your way back ... simply press %s", - keywordStyle.Render("backspace")) - } - - - return msg + "\n\n" -} - -func checkbox(label string, checked bool) string { - if checked { - return checkboxStyle.Render("+ " + label) - } - return fmt.Sprintf("- %s", label) -} - -// Utils - -// API Status Handle -func handleAPIStatusRequest(url string) tea.Cmd { - - return func() tea.Msg { - url := fmt.Sprintf("%s/status", url) - req, err := http.NewRequest("GET", url, nil) - if err != nil { - fmt.Print(err.Error()) - } - - res, err := http.DefaultClient.Do(req) - if err != nil { - - } - defer res.Body.Close() - - body, readErr := ioutil.ReadAll(res.Body) - if readErr != nil { - fmt.Print(err.Error()) - } - - var apiStatus APIStatusMsg - if err := json.Unmarshal(body, &apiStatus); err != nil { // Parse []byte to the go struct pointer - fmt.Println("Can not unmarshal JSON") - } - - - return apiStatus - } -} - -type APIStatus struct { - Status string `json:"Status"` - Description string `json:"Description"` - StartDateTime string `json:"StartDateTime"` - EstimatedEndTime string `json:"EstimatedEndTime"` -} - -type APIStatusMsg APIStatus - diff --git a/go.mod b/go.mod index afe36cd..d1ae828 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module git.christiangoeschel.com/repoman-cli go 1.23.0 require ( - github.com/charmbracelet/bubbles v0.20.0 github.com/charmbracelet/bubbletea v1.1.1 github.com/charmbracelet/lipgloss v0.13.0 + github.com/repoman-cli/screens v0.0.0-00010101000000-000000000000 ) require ( @@ -16,12 +16,20 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.15.2 // indirect + github.com/repoman-cli/lib v0.0.0-00010101000000-000000000000 // indirect + github.com/repoman-cli/themes v0.0.0-00010101000000-000000000000 // indirect github.com/rivo/uniseg v0.4.7 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.3.8 // indirect ) + +replace github.com/repoman-cli/lib => /home/cgoesche/repoman-cli/lib + +replace github.com/repoman-cli/themes => /home/cgoesche/repoman-cli/themes + +replace github.com/repoman-cli/screens => /home/cgoesche/repoman-cli/screens diff --git a/go.sum b/go.sum index daac317..161a3de 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= -github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= @@ -18,8 +16,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= diff --git a/lib/go.mod b/lib/go.mod new file mode 100644 index 0000000..b3a80d6 --- /dev/null +++ b/lib/go.mod @@ -0,0 +1,3 @@ +module git.christiangoeschel.com/repoman-cli/lib + +go 1.23.0 diff --git a/lib/progaminfo.go b/lib/progaminfo.go new file mode 100644 index 0000000..cb61824 --- /dev/null +++ b/lib/progaminfo.go @@ -0,0 +1,38 @@ +package lib + +type ProgramInfo struct { + ProgramName string + Description string + Version string + BuildID string + CreationDate string + LastModification string + Maintainers []string + License string +} + +func GetProgramInfo() *ProgramInfo{ + + maintainers := []string{ + "Christian Goeschel Ndjomouo (cgoesc2@wgu.edu)", + } + + pInfo := ProgramInfo { + ProgramName: "Repoman Client", + Description: "A simplistic terminal user interface to manage your self-hosted Repoman Git repository", + Version: "0.1.0 alpha", + BuildID: "a89d2e1", + CreationDate: "2024-09-30", + Maintainers: maintainers, + License: "Copyright 2024 Christian Goeschel - MIT License", + } + + return &pInfo +} + + + + + + + diff --git a/main b/main new file mode 100755 index 0000000..d69066c Binary files /dev/null and b/main differ diff --git a/main.go b/main.go new file mode 100644 index 0000000..a3e0e58 --- /dev/null +++ b/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "os" + "github.com/repoman-cli/screens" + + tea "github.com/charmbracelet/bubbletea" +) + +// Main +func main(){ + + p := tea.NewProgram(screens.RootScreen(0), tea.WithAltScreen()) + if _, err := p.Run(); err != nil { + fmt.Println("Error starting program:", err) + os.Exit(1) + } +} + + + + + + + + + + diff --git a/old-main.go b/old-main.go new file mode 100644 index 0000000..892f6c1 --- /dev/null +++ b/old-main.go @@ -0,0 +1,288 @@ +package main + + +import ( + "fmt" + "net/http" + "io/ioutil" + "encoding/json" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +const ( + dotChar = " • " + arrowRightChar = " → " + apiUrl = "https://repoman.christiangoeschel.com:8443" +) + +var ( + spinnerStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("69")) + keywordStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("211")) + subtleStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("241")) + checkboxStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#03A9F4")) + titleStyle = lipgloss.NewStyle().Background(lipgloss.Color("#448AFF")).Foreground(lipgloss.Color("#FFFFFF")) + statusOK = lipgloss.NewStyle().Foreground(lipgloss.Color("200")) + dotStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("236")).Render(dotChar) + mainStyle = lipgloss.NewStyle().Margin(15).Padding(10) + Altstyle = lipgloss.NewStyle().BorderStyle(lipgloss.NormalBorder()).BorderForeground(lipgloss.Color("63")) + + + mainMenuOptions = []string{ + "List all repos", + "Create a new repository", + "Delete a repository", + "Manage API Keys", + "Check API Status", + "Settings", + "Quit", + } + +) + +func main() { + + initialModel := model{0, false, false, false, APIStatusMsg{} } + + p := tea.NewProgram(initialModel, tea.WithAltScreen()) + if _, err := p.Run(); err != nil { + fmt.Println("could not start program:", err) + } + +} + +type model struct { + Choice int + Chosen bool + Loaded bool + Quitting bool + APIStatus APIStatusMsg +} + +func (m model) Init() tea.Cmd { + return nil +} + +// Main update function. +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + + // Make sure these keys always quit + if msg, ok := msg.(tea.KeyMsg); ok { + key := msg.String() + + if key == "q" || key == "esc" || key == "ctrl+c" { + m.Quitting = true + return m, tea.Quit + } else if key == "backspace" { + m.Chosen = false + } + } + + // Hand off the message and model to the appropriate update function for the + // appropriate view based on the current state. + if !m.Chosen { + return updateMainMenu(msg, m) + } + + return updateChosen(msg, m) +} + +// The main view, which just calls the appropriate sub-view +func (m model) View() string { + var s string + if m.Quitting { + return "\n See you soon my friend! \n\n" + } + if !m.Chosen { + s = mainMenuView(m) + } else { + s = chosenView(m) + } + + return mainStyle.Render("\n" + s + "\n\n") +} + +// Sub-update functions + +// Update loop for the first view where you're choosing a task. +func updateMainMenu(msg tea.Msg, m model) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyMsg: + var lastChoice int = len(mainMenuOptions) - 1 + + switch msg.String() { + + case "j", "down": + m.Choice++ + if m.Choice > lastChoice { + m.Choice = 0 + } + case "k", "up": + m.Choice-- + if m.Choice < 0 { + m.Choice = lastChoice + } + case "enter", " ": + m.Chosen = true + + if mainMenuOptions[m.Choice] == "Quit" { + return m, tea.Quit + } + + return m, nil + } + + } + + return m, nil +} + + +// Update loop for the second view after a choice has been made +func updateChosen(msg tea.Msg, m model) (tea.Model, tea.Cmd) { + + switch msg := msg.(type) { + + case tea.KeyMsg: + switch msg.String() { + case "enter": + if m.Choice == 4 { + m.APIStatus = APIStatusMsg{} + return m, handleAPIStatusRequest(apiUrl) + } + } + + case APIStatusMsg: + m.APIStatus = msg + return m, nil + + + } + + return m, nil +} + +// Sub-views + +// The first view, where you're choosing a task +func mainMenuView(m model) string { + c := m.Choice + + tpl := fmt.Sprintf("%s\n\n", titleStyle.Render("[ +-+ Repoman CLI +-+ ]")) + tpl += "Main Menu\n\n" + tpl += "%s\n\n" + tpl += fmt.Sprintf("%s\n%s\n%s\n%s\n", subtleStyle.Render("j/k, up/down: select"), subtleStyle.Render("enter, spacebar: choose"), + subtleStyle.Render("q, esc: quit"), subtleStyle.Render("backspace: main menu")) + + var opts string + for i := 0;i < len(mainMenuOptions);i++{ + opts += fmt.Sprintf("%s\n", checkbox(mainMenuOptions[i], c == i)) + } + + return fmt.Sprintf(tpl, opts) +} + +// The second view, after a task has been chosen +func chosenView(m model) string { + var msg string + + switch m.Choice { + + case 0: + msg = fmt.Sprintf("%s\n\nCool, we'll need %s and %s...", + titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), + keywordStyle.Render("libgarden"), + keywordStyle.Render("vegeutils")) + + case 1: + msg = fmt.Sprintf("%s\n\nOkay, then we should install %s and %s...", + titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), + keywordStyle.Render("marketkit"), + keywordStyle.Render("libshopping")) + + case 2: + msg = fmt.Sprintf("%s\n\nOkay, cool, then we’ll need a library. Yes, an %s.", + titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), + keywordStyle.Render("actual library")) + + case 3: + msg = fmt.Sprintf("%s\n\nOkay, cool, then we’ll need a library. Yes, an %s.", + titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), + keywordStyle.Render("actual library")) + + case 4: + + statusResponse := fmt.Sprintf("Status:%s\nDescription:%s\nStart:%s\nExpected End:%s\n", + keywordStyle.Render(m.APIStatus.Status), + keywordStyle.Render(m.APIStatus.Description), + keywordStyle.Render(m.APIStatus.StartDateTime), + keywordStyle.Render(m.APIStatus.EstimatedEndTime)) + + msg = fmt.Sprintf("%s\n\nRequesting API Server Status from:\n%s\n\nResponse:\n\n%v\n\n%s", + titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), + keywordStyle.Render(apiUrl), + statusResponse, + subtleStyle.Render("Press Enter to refresh")) + + case 5: + msg = fmt.Sprintf("%s\n\nOkay, cool, then we’ll need a library. Yes, an %s.", + titleStyle.Render("[ +-+ " + mainMenuOptions[m.Choice] + " +-+ ]"), + keywordStyle.Render("actual library")) + + default: + msg = fmt.Sprintf("Seems like you are lost.\n\nI'll help you find your way back ... simply press %s", + keywordStyle.Render("backspace")) + } + + return msg + "\n\n" +} + +func checkbox(label string, checked bool) string { + if checked { + return checkboxStyle.Render("+ " + label) + } + return fmt.Sprintf("- %s", label) +} + +// Utils + +// API Status Handle +func handleAPIStatusRequest(url string) tea.Cmd { + + return func() tea.Msg { + url := fmt.Sprintf("%s/status", url) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + fmt.Print(err.Error()) + } + + res, err := http.DefaultClient.Do(req) + if err != nil { + + } + defer res.Body.Close() + + body, readErr := ioutil.ReadAll(res.Body) + if readErr != nil { + fmt.Print(err.Error()) + } + + var apiStatus APIStatusMsg + if err := json.Unmarshal(body, &apiStatus); err != nil { // Parse []byte to the go struct pointer + fmt.Println("Can not unmarshal JSON") + } + + + return apiStatus + } +} + +type APIStatus struct { + Status string `json:"Status"` + Description string `json:"Description"` + StartDateTime string `json:"StartDateTime"` + EstimatedEndTime string `json:"EstimatedEndTime"` +} + +type APIStatusMsg APIStatus + diff --git a/screens/apistatus.go b/screens/apistatus.go new file mode 100644 index 0000000..388c5fa --- /dev/null +++ b/screens/apistatus.go @@ -0,0 +1,62 @@ +package screens + + +import ( + //"fmt" + tea "github.com/charmbracelet/bubbletea" + "github.com/repoman-cli/themes" +) + + +type APIStatusScreenModel struct { + APIStatus string + URL string + Err error + StatusCode int +} + + +// --- AltScreenModel functions + +// Creates a new mainMenuScreenModel +func NewAPIStatusScreen() APIStatusScreenModel { + return APIStatusScreenModel { + APIStatus: "Normal", + URL: "https://repoman.christiangoeschel.com:8443/status", + Err: nil, + StatusCode: 200, + } +} + +// Init +func (m *APIStatusScreenModel) Init() tea.Cmd { + return nil +} + +// Update +func (m *APIStatusScreenModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "q": + mainMenu := MainMenu() + return RootScreen(0).SwitchScreen(&mainMenu) + + } + + default: + return m, nil + } + + return m, nil +} + +// View +func (m *APIStatusScreenModel) View() string { + + msg := themes.KeywordStyle.Render("API Status") + + return msg + "\n\n" +} + diff --git a/screens/go.mod b/screens/go.mod new file mode 100644 index 0000000..62191bb --- /dev/null +++ b/screens/go.mod @@ -0,0 +1,30 @@ +module git.christiangoeschel.com/repoman-cli/screens + +go 1.23.0 + +require ( + github.com/charmbracelet/bubbletea v1.1.1 + github.com/repoman-cli/lib v0.0.0-00010101000000-000000000000 +) + +require ( + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/charmbracelet/lipgloss v0.13.0 // indirect + github.com/charmbracelet/x/ansi v0.2.3 // indirect + github.com/charmbracelet/x/term v0.2.0 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.3.8 // indirect +) + +replace github.com/repoman-cli/themes => /home/cgoesche/repoman-cli/themes +replace github.com/repoman-cli/lib => /home/cgoesche/repoman-cli/lib diff --git a/screens/go.sum b/screens/go.sum new file mode 100644 index 0000000..161a3de --- /dev/null +++ b/screens/go.sum @@ -0,0 +1,37 @@ +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= +github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= +github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= +github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= +github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY= +github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= +github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= diff --git a/screens/root.go b/screens/root.go new file mode 100644 index 0000000..5b7709f --- /dev/null +++ b/screens/root.go @@ -0,0 +1,112 @@ +package screens + +import ( + "fmt" + "github.com/repoman-cli/lib" + //"github.com/repoman-cli/themes" + + tea "github.com/charmbracelet/bubbletea" +) + + + // Root Screen Model + type rootScreenModel struct { + model tea.Model // Contains the current screen model which represents the programs current state + theme int + } + + //Main Menu Screen + type mainMenuScreenModel struct { + Title string + Message string + } + + +// Initialize model +func (m rootScreenModel) Init() tea.Cmd { + return m.model.Init() // Calls current model's Init function +} + +// Update programs state based on received messages +func (m rootScreenModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + return m.model.Update(msg) // Calls current model's Update function +} + +// Change the View of current model based on its state +func (m rootScreenModel) View() string { + return m.model.View() +} + +// --- Utils + +// Creates a new model based on what RootScreenModel.model Contains +func RootScreen(theme int) rootScreenModel { + var rootModel tea.Model + + + // Set the rootModel (current state) to be the mainMenuScreen + mainMenu := MainMenu() + rootModel = &mainMenu + + + return rootScreenModel { + model: rootModel, + theme: theme, + } +} + + +// Screen switcher sets new model and returns +func (m rootScreenModel) SwitchScreen(model tea.Model) (tea.Model, tea.Cmd) { + m.model = model + return m.model, m.model.Init() +} + + +// --- MainMenuScreenModel functions + +// Creates a new mainMenuScreenModel +func MainMenu() mainMenuScreenModel { + return mainMenuScreenModel { + Title: "Repoman CLI", + Message: "Hello my friend", + } +} + +// Init +func (m *mainMenuScreenModel) Init() tea.Cmd { + return nil +} + +// Update +func (m *mainMenuScreenModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + + switch msg := msg.(type) { + case tea.KeyMsg: + switch msg.String() { + case "q": + return m, tea.Quit + + case "n": + apiStatusScreen := NewAPIStatusScreen() + return RootScreen(0).SwitchScreen(&apiStatusScreen) + } + + + default: + return m, nil + } + + return m, nil +} + +// View +func (m *mainMenuScreenModel) View() string { + + programInfo := lib.GetProgramInfo() + + message := fmt.Sprintf("%s\n\n%s", programInfo.ProgramName, programInfo.BuildID) + return message +} + + diff --git a/themes/default-theme.go b/themes/default-theme.go new file mode 100644 index 0000000..9cf35df --- /dev/null +++ b/themes/default-theme.go @@ -0,0 +1,27 @@ +package themes + +import ( + + "github.com/charmbracelet/lipgloss" +) + +var ( + spinnerStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("69")) + KeywordStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("211")) + subtleStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("241")) + checkboxStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#03A9F4")) + titleStyle = lipgloss.NewStyle().Background(lipgloss.Color("#448AFF")).Foreground(lipgloss.Color("#FFFFFF")) + statusOK = lipgloss.NewStyle().Foreground(lipgloss.Color("200")) + mainStyle = lipgloss.NewStyle().Margin(15).Padding(10) + Altstyle = lipgloss.NewStyle().BorderStyle(lipgloss.NormalBorder()).BorderForeground(lipgloss.Color("63")) +) + +func ChangeTheme(themeID int) (err error) { + + if themeID == 0 { + KeywordStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("200")) + return nil + } + + return nil +} diff --git a/themes/go.mod b/themes/go.mod new file mode 100644 index 0000000..48db9e8 --- /dev/null +++ b/themes/go.mod @@ -0,0 +1,3 @@ +module git.christiangoeschel.com/repoman-cli/themes + +go 1.23.0