diff --git a/.gitignore b/.gitignore index dd0e828..59e018d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,19 @@ /vendor/ /Godeps/ +### VS Code +# Reference: https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets +# Local History for Visual Studio Code +.history/ +# Built Visual Studio Code Extensions +*.vsix + ### JetBrains+all ### # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..bbe40be --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // needs to attach to running dlv process execute + // dlv debug --headless --listen=:2345 . + // in the root of the package first + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to Process", + "type": "go", + "debugAdapter": "dlv-dap", + "request": "attach", + "mode": "remote", + "remotePath": "${workspaceFolder}", + "host": "127.0.0.1", + "port": 2345 + } + ] +} diff --git a/tea/components/error.go b/tea/components/error.go index 7e72593..d1369ea 100644 --- a/tea/components/error.go +++ b/tea/components/error.go @@ -32,7 +32,7 @@ func (e ErrorComponent) Init() tea.Cmd { return nil } -func (e ErrorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { +func (e ErrorComponent) Update(_ tea.Msg) (tea.Model, tea.Cmd) { return e, nil } diff --git a/tea/components/header.go b/tea/components/header.go index 62c5522..54cd599 100644 --- a/tea/components/header.go +++ b/tea/components/header.go @@ -25,7 +25,7 @@ func (h headerComponent) Init() tea.Cmd { return nil } -func (h headerComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) { +func (h headerComponent) Update(_ tea.Msg) (tea.Model, tea.Cmd) { return h, nil } diff --git a/tea/scenes/apply.go b/tea/scenes/apply.go index 03b2d51..4162fbe 100644 --- a/tea/scenes/apply.go +++ b/tea/scenes/apply.go @@ -7,6 +7,7 @@ import ( "github.com/satisfactorymodding/ficsit-cli/cli" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -149,13 +150,13 @@ func (m apply) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit - case KeyEscape: + case keys.KeyEscape: m.cancelled = true m.cancelChannel <- true return m, nil - case KeyEnter: + case keys.KeyEnter: if m.status.done { if m.parent != nil { return m.parent, m.parent.Init() diff --git a/tea/scenes/errors.go b/tea/scenes/errors/errors.go similarity index 77% rename from tea/scenes/errors.go rename to tea/scenes/errors/errors.go index bd672be..d8bd32e 100644 --- a/tea/scenes/errors.go +++ b/tea/scenes/errors/errors.go @@ -1,4 +1,4 @@ -package scenes +package errors const ( ErrorFailedAddMod = "failed to add mod" diff --git a/tea/scenes/installation.go b/tea/scenes/installation/installation.go similarity index 90% rename from tea/scenes/installation.go rename to tea/scenes/installation/installation.go index aab0db7..fe81709 100644 --- a/tea/scenes/installation.go +++ b/tea/scenes/installation/installation.go @@ -1,16 +1,16 @@ -package scenes +package installation import ( "fmt" "time" - "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/satisfactorymodding/ficsit-cli/cli" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -66,19 +66,7 @@ func NewInstallation(root components.RootModel, parent tea.Model, installationDa model.list.Styles = utils.ListStyles model.list.SetSize(model.list.Width(), model.list.Height()) model.list.StatusMessageLifetime = time.Second * 3 - model.list.DisableQuitKeybindings() - - model.list.AdditionalShortHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } - - model.list.AdditionalFullHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } + model.list.KeyMap.Quit.SetHelp("q", "back") return model } @@ -91,7 +79,7 @@ func (m installation) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit case "q": if m.parent != nil { @@ -104,7 +92,7 @@ func (m installation) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.parent, nil } return m, nil - case KeyEnter: + case keys.KeyEnter: i, ok := m.list.SelectedItem().(utils.SimpleItem[installation]) if ok { if i.Activate != nil { diff --git a/tea/scenes/installations.go b/tea/scenes/installation/installations.go similarity index 91% rename from tea/scenes/installations.go rename to tea/scenes/installation/installations.go index 2077b48..161d235 100644 --- a/tea/scenes/installations.go +++ b/tea/scenes/installation/installations.go @@ -1,4 +1,4 @@ -package scenes +package installation import ( "github.com/charmbracelet/bubbles/key" @@ -8,6 +8,7 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -28,12 +29,10 @@ func NewInstallations(root components.RootModel, parent tea.Model) tea.Model { l.Styles = utils.ListStyles l.SetSize(l.Width(), l.Height()) l.KeyMap.Quit.SetHelp("q", "back") - l.DisableQuitKeybindings() l.AdditionalShortHelpKeys = func() []key.Binding { return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - key.NewBinding(key.WithHelp("n", "new installation")), + key.NewBinding(key.WithKeys("n"), key.WithHelp("n", "new installation")), } } @@ -51,9 +50,6 @@ func (m installations) Init() tea.Cmd { } func (m installations) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - // List enables its own keybindings when they were previously disabled - m.list.DisableQuitKeybindings() - switch msg := msg.(type) { case tea.KeyMsg: if m.list.SettingFilter() { @@ -66,7 +62,7 @@ func (m installations) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "n": newModel := NewNewInstallation(m.root, m) return newModel, newModel.Init() - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit case "q": if m.parent != nil { @@ -74,7 +70,7 @@ func (m installations) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.parent, nil } return m, tea.Quit - case KeyEnter: + case keys.KeyEnter: i, ok := m.list.SelectedItem().(utils.SimpleItem[installations]) if ok { if i.Activate != nil { diff --git a/tea/scenes/installation/messages.go b/tea/scenes/installation/messages.go new file mode 100644 index 0000000..c85cf6b --- /dev/null +++ b/tea/scenes/installation/messages.go @@ -0,0 +1,15 @@ +package installation + +import tea "github.com/charmbracelet/bubbletea" + +type updateInstallationList struct{} + +func updateInstallationListCmd() tea.Msg { + return updateInstallationList{} +} + +type updateInstallationNames struct{} + +func updateInstallationNamesCmd() tea.Msg { + return updateInstallationNames{} +} diff --git a/tea/scenes/new_installation.go b/tea/scenes/installation/new_installation.go similarity index 84% rename from tea/scenes/new_installation.go rename to tea/scenes/installation/new_installation.go index cb2ffe8..331e1ad 100644 --- a/tea/scenes/new_installation.go +++ b/tea/scenes/installation/new_installation.go @@ -1,8 +1,9 @@ -package scenes +package installation import ( "fmt" "io" + "io/fs" "os" "path/filepath" "sort" @@ -18,6 +19,7 @@ import ( "github.com/sahilm/fuzzy" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -42,10 +44,16 @@ func NewNewInstallation(root components.RootModel, parent tea.Model) tea.Model { l.SetShowTitle(false) l.Styles = utils.ListStyles l.SetSize(l.Width(), l.Height()) - l.KeyMap.Quit.SetHelp("esc", "back") - l.DisableQuitKeybindings() - l.SetShowHelp(false) l.SetShowStatusBar(false) + l.KeyMap.ShowFullHelp.Unbind() + l.KeyMap.Quit.SetHelp("esc", "back") + l.KeyMap.CursorDown.SetHelp("↓", "down") + l.KeyMap.CursorUp.SetHelp("↑", "up") + l.AdditionalShortHelpKeys = func() []key.Binding { + return []key.Binding{ + key.NewBinding(key.WithKeys(keys.KeyTab), key.WithHelp(keys.KeyTab, "select")), + } + } model := newInstallation{ root: root, @@ -69,11 +77,11 @@ func (m newInstallation) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit - case KeyEscape: + case keys.KeyEscape: return m.parent, nil - case KeyEnter: + case keys.KeyEnter: newInstall, err := m.root.GetGlobal().Installations.AddInstallation(m.root.GetGlobal(), m.input.Value(), m.root.GetGlobal().Profiles.SelectedProfile) if err != nil { errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5) @@ -90,7 +98,7 @@ func (m newInstallation) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } return m.parent, updateInstallationListCmd - case KeyTab: + case keys.KeyTab: var cmd tea.Cmd m.input, cmd = m.input.Update(msg) @@ -149,7 +157,8 @@ func (m newInstallation) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m newInstallation) View() string { - inputView := lipgloss.NewStyle().Padding(1, 2).Render(m.input.View()) + style := lipgloss.NewStyle().Padding(1, 2) + inputView := style.Render(m.input.View()) mandatory := lipgloss.JoinVertical(lipgloss.Left, m.root.View(), m.title, inputView) @@ -157,19 +166,18 @@ func (m newInstallation) View() string { return lipgloss.JoinVertical(lipgloss.Left, mandatory, m.error.View()) } - if len(m.dirList.Items()) > 0 { - m.dirList.SetSize(m.dirList.Width(), m.root.Size().Height-lipgloss.Height(mandatory)-1) - return lipgloss.JoinVertical(lipgloss.Left, mandatory, m.dirList.View()) + if len(m.dirList.Items()) == 0 { + infoBox := lipgloss.NewStyle(). + BorderStyle(lipgloss.ThickBorder()). + BorderForeground(lipgloss.Color("39")). + Padding(0, 1). + Margin(0, 0, 0, 2). + Render("Enter the path to the satisfactory installation") + mandatory = lipgloss.JoinVertical(lipgloss.Left, mandatory, infoBox) } - infoBox := lipgloss.NewStyle(). - BorderStyle(lipgloss.ThickBorder()). - BorderForeground(lipgloss.Color("39")). - Padding(0, 1). - Margin(0, 0, 0, 2). - Render("Enter the path to the satisfactory installation") - - return lipgloss.JoinVertical(lipgloss.Left, mandatory, infoBox) + m.dirList.SetSize(m.dirList.Width(), m.root.Size().Height-lipgloss.Height(mandatory)-1) + return lipgloss.JoinVertical(lipgloss.Left, mandatory, m.dirList.View()) } // I know this is awful, but beats re-implementing the entire list model @@ -193,7 +201,7 @@ func getDirItems(inputValue string) []list.Item { if filter != "" { dirNames := make([]string, 0) for _, entry := range dir { - if entry.IsDir() { + if entry.IsDir() || entry.Type() == fs.ModeSymlink { dirNames = append(dirNames, entry.Name()) } } @@ -213,7 +221,7 @@ func getDirItems(inputValue string) []list.Item { globalMatches = matches } else { for _, entry := range dir { - if entry.IsDir() { + if entry.IsDir() || entry.Type() == fs.ModeSymlink { newItems = append(newItems, utils.SimpleItemExtra[newInstallation, string]{ SimpleItem: utils.SimpleItem[newInstallation]{ ItemTitle: entry.Name(), diff --git a/tea/scenes/keys.go b/tea/scenes/keys/keys.go similarity index 86% rename from tea/scenes/keys.go rename to tea/scenes/keys/keys.go index 9daaf32..56e88b5 100644 --- a/tea/scenes/keys.go +++ b/tea/scenes/keys/keys.go @@ -1,4 +1,4 @@ -package scenes +package keys const ( KeyControlC = "ctrl+c" diff --git a/tea/scenes/main_menu.go b/tea/scenes/main_menu.go index 640a6c0..03990b6 100644 --- a/tea/scenes/main_menu.go +++ b/tea/scenes/main_menu.go @@ -11,6 +11,11 @@ import ( "github.com/spf13/viper" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/errors" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/installation" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/mods" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/profile" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -63,28 +68,28 @@ func NewMainMenu(root components.RootModel) tea.Model { utils.SimpleItem[mainMenu]{ ItemTitle: "Installations", Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) { - newModel := NewInstallations(root, currentModel) + newModel := installation.NewInstallations(root, currentModel) return newModel, newModel.Init() }, }, utils.SimpleItem[mainMenu]{ ItemTitle: "Profiles", Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) { - newModel := NewProfiles(root, currentModel) + newModel := profile.NewProfiles(root, currentModel) return newModel, newModel.Init() }, }, utils.SimpleItem[mainMenu]{ ItemTitle: "All Mods", Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) { - newModel := NewMods(root, currentModel) + newModel := mods.NewMods(root, currentModel) return newModel, newModel.Init() }, }, utils.SimpleItem[mainMenu]{ ItemTitle: "Installed Mods", Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) { - newModel := NewInstalledMods(root, currentModel) + newModel := mods.NewInstalledMods(root, currentModel) return newModel, newModel.Init() }, }, @@ -92,7 +97,7 @@ func NewMainMenu(root components.RootModel) tea.Model { ItemTitle: "Apply Changes", Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) { if err := root.GetGlobal().Save(); err != nil { - log.Error().Err(err).Msg(ErrorFailedAddMod) + log.Error().Err(err).Msg(errors.ErrorFailedAddMod) errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5) currentModel.error = errorComponent return currentModel, cmd @@ -106,7 +111,7 @@ func NewMainMenu(root components.RootModel) tea.Model { ItemTitle: "Save", Activate: func(msg tea.Msg, currentModel mainMenu) (tea.Model, tea.Cmd) { if err := root.GetGlobal().Save(); err != nil { - log.Error().Err(err).Msg(ErrorFailedAddMod) + log.Error().Err(err).Msg(errors.ErrorFailedAddMod) errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5) currentModel.error = errorComponent return currentModel, cmd @@ -127,7 +132,6 @@ func NewMainMenu(root components.RootModel) tea.Model { model.list.SetFilteringEnabled(false) model.list.Title = "Main Menu" model.list.Styles = utils.ListStyles - model.list.DisableQuitKeybindings() return model } @@ -140,11 +144,11 @@ func (m mainMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit case "q": return m, tea.Quit - case KeyEnter: + case keys.KeyEnter: i, ok := m.list.SelectedItem().(utils.SimpleItem[mainMenu]) if ok { if i.Activate != nil { @@ -202,6 +206,6 @@ func (m mainMenu) View() string { return lipgloss.JoinVertical(lipgloss.Left, header, err, m.list.View()) } - m.list.SetSize(m.list.Width(), m.root.Size().Height-lipgloss.Height(header)) + m.list.SetSize(m.list.Width(), m.root.Size().Height-lipgloss.Height(header)-1) return lipgloss.JoinVertical(lipgloss.Left, header, m.list.View()) } diff --git a/tea/scenes/installed_mods.go b/tea/scenes/mods/installed_mods.go similarity index 91% rename from tea/scenes/installed_mods.go rename to tea/scenes/mods/installed_mods.go index dfe8b48..b3fcc5d 100644 --- a/tea/scenes/installed_mods.go +++ b/tea/scenes/mods/installed_mods.go @@ -1,11 +1,10 @@ -package scenes +package mods import ( "context" "sort" "time" - "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" @@ -13,6 +12,7 @@ import ( "github.com/satisfactorymodding/ficsit-cli/ficsit" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -43,19 +43,6 @@ func NewInstalledMods(root components.RootModel, parent tea.Model) tea.Model { l.Styles = utils.ListStyles l.SetSize(l.Width(), l.Height()) l.KeyMap.Quit.SetHelp("q", "back") - l.DisableQuitKeybindings() - - l.AdditionalShortHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } - - l.AdditionalFullHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } m := &installedModsList{ root: root, @@ -160,9 +147,6 @@ func (m installedModsList) LoadModData() { } func (m installedModsList) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - // List enables its own keybindings when they were previously disabled - m.list.DisableQuitKeybindings() - switch msg := msg.(type) { case tea.KeyMsg: if m.list.SettingFilter() { @@ -172,7 +156,7 @@ func (m installedModsList) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit case "q": if m.parent != nil { @@ -180,7 +164,7 @@ func (m installedModsList) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.parent, nil } return m, tea.Quit - case KeyEnter: + case keys.KeyEnter: i, ok := m.list.SelectedItem().(utils.SimpleItem[installedModsList]) if ok { return m.processActivation(i, msg) diff --git a/tea/scenes/mod.go b/tea/scenes/mods/mod.go similarity index 88% rename from tea/scenes/mod.go rename to tea/scenes/mods/mod.go index b44a017..1522f27 100644 --- a/tea/scenes/mod.go +++ b/tea/scenes/mods/mod.go @@ -1,15 +1,16 @@ -package scenes +package mods import ( "time" - "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/rs/zerolog/log" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/errors" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -70,8 +71,8 @@ func NewModMenu(root components.RootModel, parent tea.Model, mod utils.Mod) tea. Activate: func(msg tea.Msg, currentModel modMenu) (tea.Model, tea.Cmd) { err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0") if err != nil { - log.Error().Err(err).Msg(ErrorFailedAddMod) - cmd := currentModel.list.NewStatusMessage(ErrorFailedAddMod) + log.Error().Err(err).Msg(errors.ErrorFailedAddMod) + cmd := currentModel.list.NewStatusMessage(errors.ErrorFailedAddMod) return currentModel, cmd } return currentModel.parent, nil @@ -101,21 +102,8 @@ func NewModMenu(root components.RootModel, parent tea.Model, mod utils.Mod) tea. model.list.Title = mod.Name model.list.Styles = utils.ListStyles model.list.SetSize(model.list.Width(), model.list.Height()) - model.list.KeyMap.Quit.SetHelp("q", "back") model.list.StatusMessageLifetime = time.Second * 3 - model.list.DisableQuitKeybindings() - - model.list.AdditionalShortHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } - - model.list.AdditionalFullHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } + model.list.KeyMap.Quit.SetHelp("q", "back") return model } @@ -128,7 +116,7 @@ func (m modMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit case "q": if m.parent != nil { @@ -136,7 +124,7 @@ func (m modMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.parent, nil } return m, tea.Quit - case KeyEnter: + case keys.KeyEnter: i, ok := m.list.SelectedItem().(utils.SimpleItem[modMenu]) if ok { if i.Activate != nil { diff --git a/tea/scenes/mod_info.go b/tea/scenes/mods/mod_info.go similarity index 98% rename from tea/scenes/mod_info.go rename to tea/scenes/mods/mod_info.go index 800454c..8724c1d 100644 --- a/tea/scenes/mod_info.go +++ b/tea/scenes/mods/mod_info.go @@ -1,4 +1,4 @@ -package scenes +package mods import ( "context" @@ -19,6 +19,7 @@ import ( "github.com/satisfactorymodding/ficsit-cli/ficsit" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -133,7 +134,7 @@ func (m modInfo) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit case "q": if m.parent != nil { diff --git a/tea/scenes/mod_semver.go b/tea/scenes/mods/mod_semver.go similarity index 93% rename from tea/scenes/mod_semver.go rename to tea/scenes/mods/mod_semver.go index b76e8d4..63bca69 100644 --- a/tea/scenes/mod_semver.go +++ b/tea/scenes/mods/mod_semver.go @@ -1,4 +1,4 @@ -package scenes +package mods import ( "time" @@ -8,6 +8,7 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -46,11 +47,11 @@ func (m modSemver) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit - case KeyEscape: + case keys.KeyEscape: return m.parent, nil - case KeyEnter: + case keys.KeyEnter: err := m.root.GetCurrentProfile().AddMod(m.mod.Reference, m.input.Value()) if err != nil { errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5) diff --git a/tea/scenes/mod_version.go b/tea/scenes/mods/mod_version.go similarity index 85% rename from tea/scenes/mod_version.go rename to tea/scenes/mods/mod_version.go index df072c5..342a2df 100644 --- a/tea/scenes/mod_version.go +++ b/tea/scenes/mods/mod_version.go @@ -1,15 +1,16 @@ -package scenes +package mods import ( "time" - "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/rs/zerolog/log" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/errors" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -51,8 +52,8 @@ func NewModVersion(root components.RootModel, parent tea.Model, mod utils.Mod) t Activate: func(msg tea.Msg, currentModel modVersionMenu) (tea.Model, tea.Cmd) { err := root.GetCurrentProfile().AddMod(mod.Reference, ">=0.0.0") if err != nil { - log.Error().Err(err).Msg(ErrorFailedAddMod) - cmd := currentModel.list.NewStatusMessage(ErrorFailedAddMod) + log.Error().Err(err).Msg(errors.ErrorFailedAddMod) + cmd := currentModel.list.NewStatusMessage(errors.ErrorFailedAddMod) return currentModel, cmd } return currentModel.parent, nil @@ -67,21 +68,8 @@ func NewModVersion(root components.RootModel, parent tea.Model, mod utils.Mod) t model.list.Title = mod.Name model.list.Styles = utils.ListStyles model.list.SetSize(model.list.Width(), model.list.Height()) - model.list.KeyMap.Quit.SetHelp("q", "back") model.list.StatusMessageLifetime = time.Second * 3 - model.list.DisableQuitKeybindings() - - model.list.AdditionalShortHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } - - model.list.AdditionalFullHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } + model.list.KeyMap.Quit.SetHelp("q", "back") return model } @@ -94,7 +82,7 @@ func (m modVersionMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit case "q": if m.parent != nil { @@ -102,7 +90,7 @@ func (m modVersionMenu) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.parent, nil } return m, tea.Quit - case KeyEnter: + case keys.KeyEnter: i, ok := m.list.SelectedItem().(utils.SimpleItem[modVersionMenu]) if ok { if i.Activate != nil { diff --git a/tea/scenes/mods.go b/tea/scenes/mods/mods.go similarity index 94% rename from tea/scenes/mods.go rename to tea/scenes/mods/mods.go index 4ded8d0..2714f1a 100644 --- a/tea/scenes/mods.go +++ b/tea/scenes/mods/mods.go @@ -1,4 +1,4 @@ -package scenes +package mods import ( "context" @@ -17,6 +17,7 @@ import ( "github.com/satisfactorymodding/ficsit-cli/cli" "github.com/satisfactorymodding/ficsit-cli/ficsit" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -36,6 +37,8 @@ type listUpdate struct { Done bool } +// type keys + type modsList struct { list list.Model sortFieldList list.Model @@ -52,7 +55,7 @@ type modsList struct { } func NewMods(root components.RootModel, parent tea.Model) tea.Model { - l := list.New([]list.Item{}, ModsListDelegate{ + l := list.New([]list.Item{}, ListDelegate{ ItemDelegate: utils.NewItemDelegate(), Context: root.GetGlobal(), }, root.Size().Width, root.Size().Height-root.Height()) @@ -64,23 +67,14 @@ func NewMods(root components.RootModel, parent tea.Model) tea.Model { l.Styles = utils.ListStyles l.SetSize(l.Width(), l.Height()) l.KeyMap.Quit.SetHelp("q", "back") - l.DisableQuitKeybindings() l.AdditionalShortHelpKeys = func() []key.Binding { return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - key.NewBinding(key.WithHelp("s", "sort")), - key.NewBinding(key.WithHelp("o", "order")), - } - } - - l.AdditionalFullHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - key.NewBinding(key.WithHelp("s", "sort")), - key.NewBinding(key.WithHelp("o", "order")), + key.NewBinding(key.WithKeys("s"), key.WithHelp("s", "sort")), + key.NewBinding(key.WithKeys("o"), key.WithHelp("o", "order")), } } + l.AdditionalFullHelpKeys = l.AdditionalShortHelpKeys sortFieldList := list.New([]list.Item{ utils.SimpleItemExtra[modsList, ficsit.ModsModsGetModsModsMod]{ @@ -167,8 +161,6 @@ func NewMods(root components.RootModel, parent tea.Model) tea.Model { sortFieldList.Title = modsTitle sortFieldList.Styles = utils.ListStyles sortFieldList.SetSize(l.Width(), l.Height()) - sortFieldList.KeyMap.Quit.SetHelp("q", "back") - sortFieldList.DisableQuitKeybindings() sortOrderList := list.New([]list.Item{ utils.SimpleItemExtra[modsList, ficsit.ModsModsGetModsModsMod]{ @@ -200,8 +192,6 @@ func NewMods(root components.RootModel, parent tea.Model) tea.Model { sortOrderList.Title = modsTitle sortOrderList.Styles = utils.ListStyles sortOrderList.SetSize(l.Width(), l.Height()) - sortOrderList.KeyMap.Quit.SetHelp("q", "back") - sortOrderList.DisableQuitKeybindings() m := &modsList{ root: root, @@ -281,8 +271,7 @@ func (m modsList) Init() tea.Cmd { } func (m modsList) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - // List enables its own keybindings when they were previously disabled - m.list.DisableQuitKeybindings() + m.list.KeyMap.Quit.SetHelp("q", "back") switch msg := msg.(type) { case tea.KeyMsg: @@ -299,7 +288,7 @@ func (m modsList) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "o": m.showSortOrderList = !m.showSortOrderList return m, nil - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit case "q": if m.showSortFieldList { @@ -317,7 +306,7 @@ func (m modsList) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.parent, nil } return m, tea.Quit - case KeyEnter: + case keys.KeyEnter: if m.showSortFieldList { m.showSortFieldList = false i, ok := m.sortFieldList.SelectedItem().(utils.SimpleItemExtra[modsList, ficsit.ModsModsGetModsModsMod]) @@ -473,12 +462,12 @@ func ascDesc(order sortOrder, result bool) bool { return !result } -type ModsListDelegate struct { +type ListDelegate struct { list.ItemDelegate Context *cli.GlobalContext } -func (c ModsListDelegate) Render(w io.Writer, m list.Model, index int, item list.Item) { +func (c ListDelegate) Render(w io.Writer, m list.Model, index int, item list.Item) { realItem := item.(utils.SimpleItemExtra[modsList, ficsit.ModsModsGetModsModsMod]) realDelegate := c.ItemDelegate.(list.DefaultDelegate) diff --git a/tea/scenes/select_mod_version.go b/tea/scenes/mods/select_mod_version.go similarity index 92% rename from tea/scenes/select_mod_version.go rename to tea/scenes/mods/select_mod_version.go index 71ced35..0d49b33 100644 --- a/tea/scenes/select_mod_version.go +++ b/tea/scenes/mods/select_mod_version.go @@ -1,11 +1,10 @@ -package scenes +package mods import ( "context" "fmt" "time" - "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" @@ -13,6 +12,7 @@ import ( "github.com/satisfactorymodding/ficsit-cli/ficsit" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -36,19 +36,6 @@ func NewModVersionList(root components.RootModel, parent tea.Model, mod utils.Mo l.Styles = utils.ListStyles l.SetSize(l.Width(), l.Height()) l.KeyMap.Quit.SetHelp("q", "back") - l.DisableQuitKeybindings() - - l.AdditionalShortHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } - - l.AdditionalFullHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } m := &selectModVersionList{ root: root, @@ -115,7 +102,7 @@ func (m selectModVersionList) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit case "q": if m.parent != nil { @@ -123,7 +110,7 @@ func (m selectModVersionList) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.parent, nil } return m, tea.Quit - case KeyEnter: + case keys.KeyEnter: i, ok := m.list.SelectedItem().(utils.SimpleItem[selectModVersionList]) if ok { if i.Activate != nil { diff --git a/tea/scenes/messages.go b/tea/scenes/profile/messages.go similarity index 50% rename from tea/scenes/messages.go rename to tea/scenes/profile/messages.go index 40be2f7..fb18969 100644 --- a/tea/scenes/messages.go +++ b/tea/scenes/profile/messages.go @@ -1,4 +1,4 @@ -package scenes +package profile import ( tea "github.com/charmbracelet/bubbletea" @@ -15,15 +15,3 @@ type updateProfileNames struct{} func updateProfileNamesCmd() tea.Msg { return updateProfileNames{} } - -type updateInstallationList struct{} - -func updateInstallationListCmd() tea.Msg { - return updateInstallationList{} -} - -type updateInstallationNames struct{} - -func updateInstallationNamesCmd() tea.Msg { - return updateInstallationNames{} -} diff --git a/tea/scenes/new_profile.go b/tea/scenes/profile/new_profile.go similarity index 64% rename from tea/scenes/new_profile.go rename to tea/scenes/profile/new_profile.go index 458e1d2..48c1eee 100644 --- a/tea/scenes/new_profile.go +++ b/tea/scenes/profile/new_profile.go @@ -1,24 +1,45 @@ -package scenes +package profile import ( "time" + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) var _ tea.Model = (*newProfile)(nil) +type keyMap struct { + Back key.Binding + Quit key.Binding + Enter key.Binding +} + +func (k keyMap) ShortHelp() []key.Binding { + return []key.Binding{k.Enter, k.Back} +} + +func (k keyMap) FullHelp() [][]key.Binding { + return [][]key.Binding{ + {k.Enter, k.Back}, + } +} + type newProfile struct { input textinput.Model root components.RootModel parent tea.Model error *components.ErrorComponent title string + help help.Model + keys keyMap } func NewNewProfile(root components.RootModel, parent tea.Model) tea.Model { @@ -27,6 +48,20 @@ func NewNewProfile(root components.RootModel, parent tea.Model) tea.Model { parent: parent, input: textinput.New(), title: utils.NonListTitleStyle.Render("New Profile"), + help: help.New(), + keys: keyMap{ + Back: key.NewBinding( + key.WithKeys(keys.KeyEscape, keys.KeyControlC), + key.WithHelp(keys.KeyEscape, "back"), + ), + Enter: key.NewBinding( + key.WithKeys(keys.KeyEnter), + key.WithHelp(keys.KeyEnter, "create"), + ), + Quit: key.NewBinding( + key.WithKeys(keys.KeyControlC), + ), + }, } model.input.Focus() @@ -42,12 +77,12 @@ func (m newProfile) Init() tea.Cmd { func (m newProfile) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: - switch keypress := msg.String(); keypress { - case KeyControlC: + switch { + case key.Matches(msg, m.keys.Quit): return m, tea.Quit - case KeyEscape: + case key.Matches(msg, m.keys.Back): return m.parent, nil - case KeyEnter: + case key.Matches(msg, m.keys.Enter): if _, err := m.root.GetGlobal().Profiles.AddProfile(m.input.Value()); err != nil { errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5) m.error = errorComponent @@ -74,7 +109,8 @@ func (m newProfile) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m newProfile) View() string { - inputView := lipgloss.NewStyle().Padding(1, 2).Render(m.input.View()) + style := lipgloss.NewStyle().Padding(1, 2) + inputView := style.Render(m.input.View()) if m.error != nil { return lipgloss.JoinVertical(lipgloss.Left, m.root.View(), m.title, m.error.View(), inputView) @@ -87,5 +123,5 @@ func (m newProfile) View() string { Margin(0, 0, 0, 2). Render("Enter the name of the profile") - return lipgloss.JoinVertical(lipgloss.Left, m.root.View(), m.title, inputView, infoBox) + return lipgloss.JoinVertical(lipgloss.Left, m.root.View(), m.title, inputView, infoBox, style.Render(m.help.View(m.keys))) } diff --git a/tea/scenes/profile.go b/tea/scenes/profile/profile.go similarity index 90% rename from tea/scenes/profile.go rename to tea/scenes/profile/profile.go index 2e64a1a..8fde788 100644 --- a/tea/scenes/profile.go +++ b/tea/scenes/profile/profile.go @@ -1,16 +1,16 @@ -package scenes +package profile import ( "fmt" "time" - "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/satisfactorymodding/ficsit-cli/cli" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -78,19 +78,7 @@ func NewProfile(root components.RootModel, parent tea.Model, profileData *cli.Pr model.list.Styles = utils.ListStyles model.list.SetSize(model.list.Width(), model.list.Height()) model.list.StatusMessageLifetime = time.Second * 3 - model.list.DisableQuitKeybindings() - - model.list.AdditionalShortHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } - - model.list.AdditionalFullHelpKeys = func() []key.Binding { - return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - } - } + model.list.KeyMap.Quit.SetHelp("q", "back") return model } @@ -103,7 +91,7 @@ func (m profile) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit case "q": if m.parent != nil { @@ -116,7 +104,7 @@ func (m profile) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.parent, nil } return m, nil - case KeyEnter: + case keys.KeyEnter: i, ok := m.list.SelectedItem().(utils.SimpleItem[profile]) if ok { if i.Activate != nil { diff --git a/tea/scenes/profiles.go b/tea/scenes/profile/profiles.go similarity index 91% rename from tea/scenes/profiles.go rename to tea/scenes/profile/profiles.go index 9478871..8b8b01d 100644 --- a/tea/scenes/profiles.go +++ b/tea/scenes/profile/profiles.go @@ -1,4 +1,4 @@ -package scenes +package profile import ( "github.com/charmbracelet/bubbles/key" @@ -8,6 +8,7 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -28,12 +29,10 @@ func NewProfiles(root components.RootModel, parent tea.Model) tea.Model { l.Styles = utils.ListStyles l.SetSize(l.Width(), l.Height()) l.KeyMap.Quit.SetHelp("q", "back") - l.DisableQuitKeybindings() l.AdditionalShortHelpKeys = func() []key.Binding { return []key.Binding{ - key.NewBinding(key.WithHelp("q", "back")), - key.NewBinding(key.WithHelp("n", "new profile")), + key.NewBinding(key.WithKeys("n"), key.WithHelp("n", "new profile")), } } @@ -51,9 +50,6 @@ func (m profiles) Init() tea.Cmd { } func (m profiles) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - // List enables its own keybindings when they were previously disabled - m.list.DisableQuitKeybindings() - switch msg := msg.(type) { case tea.KeyMsg: if m.list.SettingFilter() { @@ -66,7 +62,7 @@ func (m profiles) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "n": newModel := NewNewProfile(m.root, m) return newModel, newModel.Init() - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit case "q": if m.parent != nil { @@ -74,7 +70,7 @@ func (m profiles) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.parent, nil } return m, tea.Quit - case KeyEnter: + case keys.KeyEnter: i, ok := m.list.SelectedItem().(utils.SimpleItem[profiles]) if ok { if i.Activate != nil { diff --git a/tea/scenes/rename_profile.go b/tea/scenes/profile/rename_profile.go similarity index 93% rename from tea/scenes/rename_profile.go rename to tea/scenes/profile/rename_profile.go index f8a108f..86fdd2c 100644 --- a/tea/scenes/rename_profile.go +++ b/tea/scenes/profile/rename_profile.go @@ -1,4 +1,4 @@ -package scenes +package profile import ( "fmt" @@ -10,6 +10,7 @@ import ( "github.com/satisfactorymodding/ficsit-cli/cli" "github.com/satisfactorymodding/ficsit-cli/tea/components" + "github.com/satisfactorymodding/ficsit-cli/tea/scenes/keys" "github.com/satisfactorymodding/ficsit-cli/tea/utils" ) @@ -48,11 +49,11 @@ func (m renameProfile) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch keypress := msg.String(); keypress { - case KeyControlC: + case keys.KeyControlC: return m, tea.Quit - case KeyEscape: + case keys.KeyEscape: return m.parent, nil - case KeyEnter: + case keys.KeyEnter: if err := m.root.GetGlobal().Profiles.RenameProfile(m.root.GetGlobal(), m.oldName, m.input.Value()); err != nil { errorComponent, cmd := components.NewErrorComponent(err.Error(), time.Second*5) m.error = errorComponent