From cb0ec895523b7c34b292dde24e68af34480ddeea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Wi=C4=99cek?= Date: Tue, 28 May 2024 17:51:37 +0200 Subject: [PATCH] fix: keymap interface (#116) --- ui/common/keymaps.go | 18 ++-- ui/common/types.go | 2 +- ui/components/folders-list/folders.go | 88 ++++++++++++++++--- ui/components/lists-list/lists.go | 83 +++++++++++++++-- ui/components/spaces-list/spaces.go | 84 ++++++++++++++++-- ui/components/table-tasks/taskstable.go | 71 ++++++++++----- ui/components/tasks-sidebar/tasksidebar.go | 22 ++++- ui/components/views-tabs/viewstabs.go | 61 +++++++------ .../workspaces-list/workspaceslist.go | 82 +++++++++++++++-- ui/ui.go | 4 +- ui/views/compact/compact.go | 10 +-- ui/widgets/navigator/widget.go | 12 +-- ui/widgets/tasks/tasks.go | 16 ++-- 13 files changed, 433 insertions(+), 120 deletions(-) diff --git a/ui/common/keymaps.go b/ui/common/keymaps.go index 47d2a49..ab6293e 100644 --- a/ui/common/keymaps.go +++ b/ui/common/keymaps.go @@ -7,8 +7,8 @@ import ( "github.com/charmbracelet/bubbles/key" ) -func NewEmptyKeyMap() help.KeyMap { - return NewKeyMap( +func NewEmptyHelp() help.KeyMap { + return NewHelp( func() [][]key.Binding { return [][]key.Binding{} }, func() []key.Binding { return []key.Binding{} }, ) @@ -19,20 +19,20 @@ var KeyBindingBack = key.NewBinding( key.WithHelp("escape", "back to previous view"), ) -type KeyMap struct { +type Help struct { fullHelp func() [][]key.Binding shortHelp func() []key.Binding } -func NewKeyMap(fullHelp func() [][]key.Binding, shortHelp func() []key.Binding) KeyMap { - return KeyMap{ +func NewHelp(fullHelp func() [][]key.Binding, shortHelp func() []key.Binding) Help { + return Help{ fullHelp: fullHelp, shortHelp: shortHelp, } } -func (km KeyMap) With(kb key.Binding) KeyMap { - return NewKeyMap( +func (km Help) With(kb key.Binding) Help { + return NewHelp( func() [][]key.Binding { return append(km.FullHelp(), []key.Binding{kb}) }, @@ -42,11 +42,11 @@ func (km KeyMap) With(kb key.Binding) KeyMap { ) } -func (km KeyMap) FullHelp() [][]key.Binding { +func (km Help) FullHelp() [][]key.Binding { return km.fullHelp() } -func (km KeyMap) ShortHelp() []key.Binding { +func (km Help) ShortHelp() []key.Binding { return km.shortHelp() } diff --git a/ui/common/types.go b/ui/common/types.go index 7fd3e58..08b6ab7 100644 --- a/ui/common/types.go +++ b/ui/common/types.go @@ -16,7 +16,7 @@ type UIElement interface { BubblesElem Id() Id - KeyMap() help.KeyMap + Help() help.KeyMap SetSize(Size) Size() Size } diff --git a/ui/components/folders-list/folders.go b/ui/components/folders-list/folders.go index 34749c5..d17009a 100644 --- a/ui/components/folders-list/folders.go +++ b/ui/components/folders-list/folders.go @@ -2,6 +2,7 @@ package folderslist import ( "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/log" @@ -20,16 +21,72 @@ type Model struct { log *log.Logger SelectedFolder clickup.Folder folders []clickup.Folder + keyMap KeyMap +} + +type KeyMap struct { + CursorUp key.Binding + CursorUpAndSelect key.Binding + CursorDown key.Binding + CursorDownAndSelect key.Binding + Select key.Binding +} + +func (m Model) KeyMap() KeyMap { + return m.keyMap } func (m Model) Id() common.Id { return m.id } -func (m Model) KeyMap() help.KeyMap { - return common.NewKeyMap( - m.list.FullHelp, - m.list.ShortHelp, +func DefaultKeyMap() KeyMap { + return KeyMap{ + CursorUp: key.NewBinding( + key.WithKeys("k", "up"), + key.WithHelp("k, up", "up"), + ), + CursorUpAndSelect: key.NewBinding( + key.WithKeys("K", "shift+up"), + key.WithHelp("K, shift+up", "up and select"), + ), + CursorDown: key.NewBinding( + key.WithKeys("j", "down"), + key.WithHelp("j, down", "down"), + ), + CursorDownAndSelect: key.NewBinding( + key.WithKeys("J", "shift+down"), + key.WithHelp("J, down", "down and select"), + ), + Select: key.NewBinding( + key.WithKeys("enter"), + key.WithHelp("enter", "select"), + ), + } +} + +func (m Model) Help() help.KeyMap { + return common.NewHelp( + func() [][]key.Binding { + return append( + m.list.FullHelp(), + []key.Binding{ + m.keyMap.CursorUp, + m.keyMap.CursorUpAndSelect, + m.keyMap.CursorDown, + m.keyMap.CursorDownAndSelect, + m.keyMap.Select, + }, + ) + }, + func() []key.Binding { + return append( + m.list.ShortHelp(), + m.keyMap.CursorUp, + m.keyMap.CursorDown, + m.keyMap.Select, + ) + }, ).With(common.KeyBindingBack) } @@ -37,7 +94,11 @@ func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { l := list.New([]list.Item{}, list.NewDefaultDelegate(), 0, 0) + l.KeyMap.Quit.Unbind() + l.KeyMap.CursorUp.Unbind() + l.KeyMap.CursorDown.Unbind() + l.SetShowHelp(false) l.Title = "Folders" @@ -49,6 +110,7 @@ func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { ctx: ctx, SelectedFolder: clickup.Folder{}, folders: []clickup.Folder{}, + keyMap: DefaultKeyMap(), log: log, } } @@ -77,8 +139,8 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { switch msg := msg.(type) { case tea.KeyMsg: - switch keypress := msg.String(); keypress { - case "enter": + switch { + case key.Matches(msg, m.keyMap.Select): if m.list.SelectedItem() == nil { m.log.Info("List is empty") break @@ -88,8 +150,11 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { m.SelectedFolder = selectedFolder return FolderChangeCmd(selectedFolder.Id) - case "J", "shift+down": - m.list.CursorDown() + case key.Matches(msg, m.keyMap.CursorUp): + m.list.CursorUp() + + case key.Matches(msg, m.keyMap.CursorUpAndSelect): + m.list.CursorUp() if m.list.SelectedItem() == nil { m.log.Info("List is empty") break @@ -99,8 +164,11 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { m.SelectedFolder = selectedFolder return common.FolderPreviewCmd(selectedFolder.Id) - case "K", "shift+up": - m.list.CursorUp() + case key.Matches(msg, m.keyMap.CursorDown): + m.list.CursorDown() + + case key.Matches(msg, m.keyMap.CursorDownAndSelect): + m.list.CursorDown() if m.list.SelectedItem() == nil { m.log.Info("List is empty") break diff --git a/ui/components/lists-list/lists.go b/ui/components/lists-list/lists.go index 8e64cd6..01c7678 100644 --- a/ui/components/lists-list/lists.go +++ b/ui/components/lists-list/lists.go @@ -2,6 +2,7 @@ package listslist import ( "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/log" @@ -20,6 +21,7 @@ type Model struct { log *log.Logger SelectedList clickup.List lists []clickup.List + keyMap KeyMap } func (m Model) Id() common.Id { @@ -30,7 +32,11 @@ func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { l := list.New([]list.Item{}, list.NewDefaultDelegate(), 0, 0) + l.KeyMap.Quit.Unbind() + l.KeyMap.CursorUp.Unbind() + l.KeyMap.CursorDown.Unbind() + l.SetShowHelp(false) l.Title = "Lists" @@ -46,10 +52,65 @@ func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { } } -func (m Model) KeyMap() help.KeyMap { - return common.NewKeyMap( - m.list.FullHelp, - m.list.ShortHelp, +type KeyMap struct { + CursorUp key.Binding + CursorUpAndSelect key.Binding + CursorDown key.Binding + CursorDownAndSelect key.Binding + Select key.Binding +} + +func DefaultKeyMap() KeyMap { + return KeyMap{ + CursorUp: key.NewBinding( + key.WithKeys("k", "up"), + key.WithHelp("k, up", "up"), + ), + CursorUpAndSelect: key.NewBinding( + key.WithKeys("K", "shift+up"), + key.WithHelp("K, shift+up", "up and select"), + ), + CursorDown: key.NewBinding( + key.WithKeys("j", "down"), + key.WithHelp("j, down", "down"), + ), + CursorDownAndSelect: key.NewBinding( + key.WithKeys("J", "shift+down"), + key.WithHelp("J, down", "down and select"), + ), + Select: key.NewBinding( + key.WithKeys("enter"), + key.WithHelp("enter", "select"), + ), + } +} + +func (m Model) KeyMap() KeyMap { + return m.keyMap +} + +func (m Model) Help() help.KeyMap { + return common.NewHelp( + func() [][]key.Binding { + return append( + m.list.FullHelp(), + []key.Binding{ + m.keyMap.CursorUp, + m.keyMap.CursorUpAndSelect, + m.keyMap.CursorDown, + m.keyMap.CursorDownAndSelect, + m.keyMap.Select, + }, + ) + }, + func() []key.Binding { + return append( + m.list.ShortHelp(), + m.keyMap.CursorUp, + m.keyMap.CursorDown, + m.keyMap.Select, + ) + }, ).With(common.KeyBindingBack) } @@ -70,8 +131,8 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { switch msg := msg.(type) { case tea.KeyMsg: - switch keypress := msg.String(); keypress { - case "enter": + switch { + case key.Matches(msg, m.keyMap.Select): if m.list.SelectedItem() == nil { m.log.Info("List is empty") break @@ -81,7 +142,10 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { m.SelectedList = selectedList return ListChangedCmd(m.SelectedList.Id) - case "J", "shift+down": + case key.Matches(msg, m.keyMap.CursorDown): + m.list.CursorDown() + + case key.Matches(msg, m.keyMap.CursorDownAndSelect): m.list.CursorDown() if m.list.SelectedItem() == nil { m.log.Info("List is empty") @@ -92,7 +156,10 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { m.SelectedList = selectedList return common.ListPreviewCmd(m.SelectedList.Id) - case "K", "shift+up": + case key.Matches(msg, m.keyMap.CursorUp): + m.list.CursorUp() + + case key.Matches(msg, m.keyMap.CursorUpAndSelect): m.list.CursorUp() if m.list.SelectedItem() == nil { m.log.Info("List is empty") diff --git a/ui/components/spaces-list/spaces.go b/ui/components/spaces-list/spaces.go index c49aedf..10b7067 100644 --- a/ui/components/spaces-list/spaces.go +++ b/ui/components/spaces-list/spaces.go @@ -2,6 +2,7 @@ package spaceslist import ( "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/log" @@ -20,16 +21,72 @@ type Model struct { log *log.Logger SelectedSpace clickup.Space spaces []clickup.Space + keyMap KeyMap } func (m Model) Id() common.Id { return m.id } -func (m Model) KeyMap() help.KeyMap { - return common.NewKeyMap( - m.list.FullHelp, - m.list.ShortHelp, +type KeyMap struct { + CursorUp key.Binding + CursorUpAndSelect key.Binding + CursorDown key.Binding + CursorDownAndSelect key.Binding + Select key.Binding +} + +func (m Model) KeyMap() KeyMap { + return m.keyMap +} + +func DefaultKeyMap() KeyMap { + return KeyMap{ + CursorUp: key.NewBinding( + key.WithKeys("k", "up"), + key.WithHelp("k, up", "up"), + ), + CursorUpAndSelect: key.NewBinding( + key.WithKeys("K", "shift+up"), + key.WithHelp("K, shift+up", "up and select"), + ), + CursorDown: key.NewBinding( + key.WithKeys("j", "down"), + key.WithHelp("j, down", "down"), + ), + CursorDownAndSelect: key.NewBinding( + key.WithKeys("J", "shift+down"), + key.WithHelp("J, down", "down and select"), + ), + Select: key.NewBinding( + key.WithKeys("enter"), + key.WithHelp("enter", "select"), + ), + } +} + +func (m Model) Help() help.KeyMap { + return common.NewHelp( + func() [][]key.Binding { + return append( + m.list.FullHelp(), + []key.Binding{ + m.keyMap.CursorUp, + m.keyMap.CursorUpAndSelect, + m.keyMap.CursorDown, + m.keyMap.CursorDownAndSelect, + m.keyMap.Select, + }, + ) + }, + func() []key.Binding { + return append( + m.list.ShortHelp(), + m.keyMap.CursorUp, + m.keyMap.CursorDown, + m.keyMap.Select, + ) + }, ).With(common.KeyBindingBack) } @@ -37,7 +94,11 @@ func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { l := list.New([]list.Item{}, list.NewDefaultDelegate(), 0, 0) + l.KeyMap.Quit.Unbind() + l.KeyMap.CursorUp.Unbind() + l.KeyMap.CursorDown.Unbind() + l.SetShowHelp(false) l.Title = "Spaces" @@ -50,6 +111,7 @@ func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { SelectedSpace: clickup.Space{}, spaces: []clickup.Space{}, log: log, + keyMap: DefaultKeyMap(), } } @@ -77,8 +139,8 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { switch msg := msg.(type) { case tea.KeyMsg: - switch keypress := msg.String(); keypress { - case "enter": + switch { + case key.Matches(msg, m.keyMap.Select): if m.list.SelectedItem() == nil { m.log.Info("List is empty") break @@ -88,7 +150,10 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { m.SelectedSpace = selectedSpace return SpaceChangedCmd(selectedSpace.Id) - case "J", "shift+down": + case key.Matches(msg, m.keyMap.CursorDown): + m.list.CursorDown() + + case key.Matches(msg, m.keyMap.CursorDownAndSelect): m.list.CursorDown() if m.list.SelectedItem() == nil { m.log.Info("List is empty") @@ -99,7 +164,10 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { m.SelectedSpace = selectedSpace return common.SpacePreviewCmd(selectedSpace.Id) - case "K", "shift+up": + case key.Matches(msg, m.keyMap.CursorUp): + m.list.CursorUp() + + case key.Matches(msg, m.keyMap.CursorUpAndSelect): m.list.CursorUp() if m.list.SelectedItem() == nil { m.log.Info("List is empty") diff --git a/ui/components/table-tasks/taskstable.go b/ui/components/table-tasks/taskstable.go index 644dc00..9e33407 100644 --- a/ui/components/table-tasks/taskstable.go +++ b/ui/components/table-tasks/taskstable.go @@ -28,6 +28,7 @@ type Model struct { Focused bool Hidden bool ifBorders bool + keyMap KeyMap } func (m Model) Id() common.Id { @@ -77,41 +78,70 @@ func (m *Model) setTableSize(s common.Size) { WithPageSize(pageSize) } -func (m Model) KeyMap() help.KeyMap { - km := m.table.KeyMap() +type KeyMap struct { + table.KeyMap +} + +func (m Model) KeyMap() KeyMap { + return m.keyMap +} + +func DefaultKeyMap() KeyMap { + km := table.DefaultKeyMap() + + return KeyMap{ + KeyMap: table.KeyMap{ + RowDown: common.KeyBindingWithHelp(km.RowDown, "down"), + RowUp: common.KeyBindingWithHelp(km.RowUp, "up"), + RowSelectToggle: common.KeyBindingWithHelp(km.RowSelectToggle, "select"), + PageDown: common.KeyBindingWithHelp(km.PageDown, "next page"), + PageUp: common.KeyBindingWithHelp(km.PageUp, "previous page"), + PageFirst: common.KeyBindingWithHelp(km.PageFirst, "first page"), + PageLast: common.KeyBindingWithHelp(km.PageLast, "last page"), + Filter: common.KeyBindingWithHelp(km.Filter, "filter"), + FilterBlur: common.KeyBindingWithHelp(km.FilterBlur, "filter blur"), + FilterClear: common.KeyBindingWithHelp(km.FilterClear, "filter clear"), + ScrollRight: common.KeyBindingWithHelp(km.ScrollRight, "scroll right"), + ScrollLeft: common.KeyBindingWithHelp(km.ScrollLeft, "scroll left"), + }, + } +} + +func (m Model) Help() help.KeyMap { + km := m.keyMap - return common.NewKeyMap( + return common.NewHelp( func() [][]key.Binding { return [][]key.Binding{ { - common.KeyBindingWithHelp(km.RowDown, "down"), - common.KeyBindingWithHelp(km.RowUp, "up"), - common.KeyBindingWithHelp(km.RowSelectToggle, "select"), + km.RowDown, + km.RowUp, + km.RowSelectToggle, }, { - common.KeyBindingWithHelp(km.PageDown, "next page"), - common.KeyBindingWithHelp(km.PageUp, "previous page"), - common.KeyBindingWithHelp(km.PageFirst, "first page"), - common.KeyBindingWithHelp(km.PageLast, "last page"), + km.PageDown, + km.PageUp, + km.PageFirst, + km.PageLast, }, { - common.KeyBindingWithHelp(km.Filter, "filter"), - common.KeyBindingWithHelp(km.FilterBlur, "filter blur"), - common.KeyBindingWithHelp(km.FilterClear, "filter clear"), + km.Filter, + km.FilterBlur, + km.FilterClear, }, { - common.KeyBindingWithHelp(km.ScrollRight, "scroll right"), - common.KeyBindingWithHelp(km.ScrollLeft, "scroll left"), + km.ScrollRight, + km.ScrollLeft, }, } }, func() []key.Binding { return []key.Binding{ - common.KeyBindingWithHelp(km.RowDown, "down"), - common.KeyBindingWithHelp(km.RowUp, "up"), - common.KeyBindingWithHelp(km.RowSelectToggle, "select"), - common.KeyBindingWithHelp(km.PageDown, "next page"), - common.KeyBindingWithHelp(km.PageUp, "previous page"), + km.RowDown, + km.RowUp, + km.RowSelectToggle, + km.PageDown, + km.PageUp, } }, ) @@ -188,6 +218,7 @@ func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { Hidden: false, log: log, ifBorders: true, + keyMap: DefaultKeyMap(), } } diff --git a/ui/components/tasks-sidebar/tasksidebar.go b/ui/components/tasks-sidebar/tasksidebar.go index a3bb2aa..13dabba 100644 --- a/ui/components/tasks-sidebar/tasksidebar.go +++ b/ui/components/tasks-sidebar/tasksidebar.go @@ -30,12 +30,27 @@ type Model struct { Hidden bool Ready bool ifBorders bool + keyMap KeyMap } func (m Model) Id() common.Id { return m.id } +type KeyMap struct { + viewport.KeyMap +} + +func (m Model) KeyMap() KeyMap { + return m.keyMap +} + +func DefaultKeyMap() KeyMap { + return KeyMap{ + KeyMap: viewport.DefaultKeyMap(), + } +} + func (m *Model) SetSize(s common.Size) { if m.ifBorders { s.Width -= 2 // two borders @@ -51,10 +66,10 @@ func (m *Model) SetSize(s common.Size) { m.viewport.SetContent(task) } -func (m Model) KeyMap() help.KeyMap { - km := m.viewport.KeyMap +func (m Model) Help() help.KeyMap { + km := m.keyMap - return common.NewKeyMap( + return common.NewHelp( func() [][]key.Binding { return [][]key.Binding{ { @@ -106,6 +121,7 @@ func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { log: log, ifBorders: true, size: size, + keyMap: DefaultKeyMap(), } } diff --git a/ui/components/views-tabs/viewstabs.go b/ui/components/views-tabs/viewstabs.go index df216ae..f451f48 100644 --- a/ui/components/views-tabs/viewstabs.go +++ b/ui/components/views-tabs/viewstabs.go @@ -44,13 +44,15 @@ func (m *Model) SetSize(s common.Size) { m.size = s } -func (m Model) KeyMap() help.KeyMap { - return common.NewKeyMap( +func (m Model) Help() help.KeyMap { + return common.NewHelp( func() [][]key.Binding { return [][]key.Binding{ { m.keyMap.CursorLeft, + m.keyMap.CursorLeftAndSelect, m.keyMap.CursorRight, + m.keyMap.CursorRightAndSelect, m.keyMap.Select, }, } @@ -83,50 +85,42 @@ func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { } func (m *Model) Update(msg tea.Msg) tea.Cmd { - var cmd tea.Cmd - var cmds []tea.Cmd - - cmds = append(cmds, cmd) - switch msg := msg.(type) { case tea.KeyMsg: - switch keypress := msg.String(); keypress { - case "H", "shift+left": + switch { + case key.Matches(msg, m.keyMap.CursorLeft): index := prevTab(m.tabs, m.SelectedTabIdx) m.SelectedTabIdx = index m.SelectedTab = m.tabs[index].Id - return TabChangedCmd(m.SelectedTab) + return nil - case "L", "shift+right": + case key.Matches(msg, m.keyMap.CursorRight): index := nextTab(m.tabs, m.SelectedTabIdx) m.SelectedTabIdx = index m.SelectedTab = m.tabs[index].Id - return TabChangedCmd(m.SelectedTab) + return nil - case "h", "left": - index := prevTab(m.tabs, m.SelectedTabIdx) + case key.Matches(msg, m.keyMap.Select): + index := nextTab(m.tabs, m.SelectedTabIdx) m.SelectedTabIdx = index m.SelectedTab = m.tabs[index].Id - return nil + return TabChangedCmd(m.SelectedTab) - case "l", "right": - index := nextTab(m.tabs, m.SelectedTabIdx) + case key.Matches(msg, m.keyMap.CursorLeftAndSelect): + index := prevTab(m.tabs, m.SelectedTabIdx) m.SelectedTabIdx = index m.SelectedTab = m.tabs[index].Id - return nil + return TabChangedCmd(m.SelectedTab) - case "enter": + case key.Matches(msg, m.keyMap.CursorRightAndSelect): index := nextTab(m.tabs, m.SelectedTabIdx) m.SelectedTabIdx = index m.SelectedTab = m.tabs[index].Id return TabChangedCmd(m.SelectedTab) - - default: - return nil } } - return tea.Batch(cmds...) + return nil } func (m Model) View() string { @@ -198,10 +192,11 @@ func (m Model) Init() tea.Cmd { } type KeyMap struct { - CursorLeft key.Binding - CursorRight key.Binding - Select key.Binding - SwitchFocusToTasks key.Binding + CursorLeft key.Binding + CursorLeftAndSelect key.Binding + CursorRight key.Binding + CursorRightAndSelect key.Binding + Select key.Binding } func DefaultKeyMap() KeyMap { @@ -210,18 +205,22 @@ func DefaultKeyMap() KeyMap { key.WithKeys("h", "left"), key.WithHelp("h, left", "previous tab"), ), + CursorLeftAndSelect: key.NewBinding( + key.WithKeys("H", "left"), + key.WithHelp("H, shift+left", "select tab"), + ), CursorRight: key.NewBinding( key.WithKeys("l", "right"), key.WithHelp("l, right", "next tab"), ), + CursorRightAndSelect: key.NewBinding( + key.WithKeys("L", "shift+right"), + key.WithHelp("L, shift+right", "select tab"), + ), Select: key.NewBinding( key.WithKeys("enter"), key.WithHelp("enter", "select"), ), - SwitchFocusToTasks: key.NewBinding( - key.WithKeys("j", "k", "escape"), - key.WithHelp("j/k/escape", "switch focus to tasks table"), - ), } } diff --git a/ui/components/workspaces-list/workspaceslist.go b/ui/components/workspaces-list/workspaceslist.go index d306ad4..74d9b01 100644 --- a/ui/components/workspaces-list/workspaceslist.go +++ b/ui/components/workspaces-list/workspaceslist.go @@ -2,6 +2,7 @@ package workspaceslist import ( "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/log" @@ -22,12 +23,50 @@ type Model struct { workspaces []clickup.Workspace ifBorders bool Focused bool + keyMap KeyMap } func (m Model) Id() common.Id { return m.id } +type KeyMap struct { + CursorUp key.Binding + CursorUpAndSelect key.Binding + CursorDown key.Binding + CursorDownAndSelect key.Binding + Select key.Binding +} + +func (m Model) KeyMap() KeyMap { + return m.keyMap +} + +func DefaultKeyMap() KeyMap { + return KeyMap{ + CursorUp: key.NewBinding( + key.WithKeys("k", "up"), + key.WithHelp("k, up", "up"), + ), + CursorUpAndSelect: key.NewBinding( + key.WithKeys("K", "shift+up"), + key.WithHelp("K, shift+up", "up and select"), + ), + CursorDown: key.NewBinding( + key.WithKeys("j", "down"), + key.WithHelp("j, down", "down"), + ), + CursorDownAndSelect: key.NewBinding( + key.WithKeys("J", "shift+down"), + key.WithHelp("J, down", "down and select"), + ), + Select: key.NewBinding( + key.WithKeys("enter"), + key.WithHelp("enter", "select"), + ), + } +} + func (m *Model) SetFocused(f bool) { m.Focused = f } @@ -37,11 +76,29 @@ func (m Model) WithFocused(f bool) Model { return m } -func (m Model) KeyMap() help.KeyMap { - return common.NewKeyMap( - m.list.FullHelp, - m.list.ShortHelp, - ) +func (m Model) Help() help.KeyMap { + return common.NewHelp( + func() [][]key.Binding { + return append( + m.list.FullHelp(), + []key.Binding{ + m.keyMap.CursorUp, + m.keyMap.CursorUpAndSelect, + m.keyMap.CursorDown, + m.keyMap.CursorDownAndSelect, + m.keyMap.Select, + }, + ) + }, + func() []key.Binding { + return append( + m.list.ShortHelp(), + m.keyMap.CursorUp, + m.keyMap.CursorDown, + m.keyMap.Select, + ) + }, + ).With(common.KeyBindingBack) } func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { @@ -66,6 +123,7 @@ func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { log: log, ifBorders: true, Focused: false, + keyMap: DefaultKeyMap(), } } @@ -98,8 +156,8 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { switch msg := msg.(type) { case tea.KeyMsg: - switch keypress := msg.String(); keypress { - case "enter": + switch { + case key.Matches(msg, m.keyMap.Select): if m.list.SelectedItem() == nil { m.log.Info("List is empty") break @@ -109,7 +167,10 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { m.SelectedWorkspace = selectedWorkspace return common.WorkspaceChangeCmd(selectedWorkspace.Id) - case "J", "shift+down": + case key.Matches(msg, m.keyMap.CursorDown): + m.list.CursorDown() + + case key.Matches(msg, m.keyMap.CursorDownAndSelect): m.list.CursorDown() if m.list.SelectedItem() == nil { m.log.Info("List is empty") @@ -120,7 +181,10 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd { m.SelectedWorkspace = selectedWorkspace return common.WorkspacePreviewCmd(selectedWorkspace.Id) - case "K", "shift+up": + case key.Matches(msg, m.keyMap.CursorUp): + m.list.CursorUp() + + case key.Matches(msg, m.keyMap.CursorUpAndSelect): m.list.CursorUp() if m.list.SelectedItem() == nil { m.log.Info("List is empty") diff --git a/ui/ui.go b/ui/ui.go index b2c8d1e..2be09ff 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -86,9 +86,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) View() string { var viewToRender common.UIElement = m.viewCompact - viewKm := viewToRender.KeyMap() + viewKm := viewToRender.Help() - km := common.NewKeyMap( + km := common.NewHelp( viewKm.FullHelp, viewKm.ShortHelp, ) diff --git a/ui/views/compact/compact.go b/ui/views/compact/compact.go index 93e64c1..abd4def 100644 --- a/ui/views/compact/compact.go +++ b/ui/views/compact/compact.go @@ -49,16 +49,16 @@ func (m Model) Init() tea.Cmd { ) } -func (m Model) KeyMap() help.KeyMap { +func (m Model) Help() help.KeyMap { switch m.state { case m.widgetNavigator.Id(): - return m.widgetNavigator.KeyMap() + return m.widgetNavigator.Help() case m.widgetViewsTabs.Id(): - return m.widgetTasks.KeyMap() + return m.widgetViewsTabs.Help() case m.widgetTasks.Id(): - return m.widgetTasks.KeyMap() + return m.widgetTasks.Help() default: - return common.NewEmptyKeyMap() + return common.NewEmptyHelp() } } diff --git a/ui/widgets/navigator/widget.go b/ui/widgets/navigator/widget.go index bfc6f14..231834c 100644 --- a/ui/widgets/navigator/widget.go +++ b/ui/widgets/navigator/widget.go @@ -103,18 +103,18 @@ func InitialModel(ctx *context.UserContext, logger *log.Logger) Model { } } -func (m Model) KeyMap() help.KeyMap { +func (m Model) Help() help.KeyMap { switch m.state { case m.componentWorkspacesList.Id(): - return m.componentWorkspacesList.KeyMap() + return m.componentWorkspacesList.Help() case m.componentSpacesList.Id(): - return m.componentSpacesList.KeyMap() + return m.componentSpacesList.Help() case m.componentFoldersList.Id(): - return m.componentFoldersList.KeyMap() + return m.componentFoldersList.Help() case m.componentListsList.Id(): - return m.componentListsList.KeyMap() + return m.componentListsList.Help() default: - return common.NewEmptyKeyMap() + return common.NewEmptyHelp() } } diff --git a/ui/widgets/tasks/tasks.go b/ui/widgets/tasks/tasks.go index 1942059..149db8a 100644 --- a/ui/widgets/tasks/tasks.go +++ b/ui/widgets/tasks/tasks.go @@ -127,11 +127,11 @@ func DefaultKeyMap() KeyMap { } } -func (m Model) KeyMap() help.KeyMap { - var km help.KeyMap +func (m Model) Help() help.KeyMap { + var help help.KeyMap if m.copyMode { - return common.NewKeyMap( + return common.NewHelp( func() [][]key.Binding { return [][]key.Binding{ { @@ -157,15 +157,15 @@ func (m Model) KeyMap() help.KeyMap { switch m.state { case m.componenetTasksSidebar.Id(): - km = m.componenetTasksSidebar.KeyMap() + help = m.componenetTasksSidebar.Help() case m.componenetTasksTable.Id(): - km = m.componenetTasksTable.KeyMap() + help = m.componenetTasksTable.Help() } - return common.NewKeyMap( + return common.NewHelp( func() [][]key.Binding { return append( - km.FullHelp(), + help.FullHelp(), []key.Binding{ m.keyMap.OpenTicketInWebBrowser, m.keyMap.ToggleSidebar, @@ -174,7 +174,7 @@ func (m Model) KeyMap() help.KeyMap { }, func() []key.Binding { return append( - km.ShortHelp(), + help.ShortHelp(), m.keyMap.OpenTicketInWebBrowser, m.keyMap.CopyMode, )