From 5543a9a8e0a6657259064a3bebef137cf5da0874 Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Sun, 13 Jul 2025 20:56:16 -0700 Subject: [PATCH] jerry-rig a help toggle cmd --- internal/app/app.go | 18 +++++++++++++-- internal/app/edit_cell.go | 34 +++++++++++++++++++--------- internal/app/query.go | 23 ++++++++++++++----- internal/app/query_keys.go | 7 ++++-- internal/app/row_detail.go | 39 ++++++++++++++++++++++----------- internal/app/row_detail_keys.go | 7 ++++-- internal/app/table_data.go | 39 ++++++++++++++++++++++----------- internal/app/table_data_keys.go | 7 ++++-- internal/app/table_list.go | 21 ++++++++++++++---- internal/app/table_list_keys.go | 7 ++++-- 10 files changed, 147 insertions(+), 55 deletions(-) diff --git a/internal/app/app.go b/internal/app/app.go index 67c8ffb..ea38725 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -28,8 +28,9 @@ type blinkMsg struct{} // AppKeyMap defines the keybindings for the application type AppKeyMap struct { - Quit key.Binding - Suspend key.Binding + Quit key.Binding + Suspend key.Binding + ToggleHelp key.Binding } // DefaultAppKeyMap returns the default keybindings @@ -43,6 +44,10 @@ func DefaultAppKeyMap() AppKeyMap { key.WithKeys("ctrl+z"), key.WithHelp("ctrl+z", "suspend"), ), + ToggleHelp: key.NewBinding( + key.WithKeys("ctrl+g"), + key.WithHelp("ctrl+g", "toggle help"), + ), } } @@ -57,6 +62,7 @@ type ( SwitchToQueryMsg struct{} ReturnToQueryMsg struct{} // Return to query mode from row detail RefreshDataMsg struct{} + ToggleHelpMsg struct{} // Toggle between short and full help UpdateCellMsg struct { RowIndex, ColIndex int Value string @@ -594,6 +600,8 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Quit case key.Matches(msg, m.keyMap.Suspend): return m, tea.Suspend + case key.Matches(msg, m.keyMap.ToggleHelp): + return m, func() tea.Msg { return ToggleHelpMsg{} } } case SwitchToTableListMsg: @@ -667,6 +675,12 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { queryModel.handleQueryCompletion(msg) } return m, nil + + case ToggleHelpMsg: + // Forward the help toggle to the current view + var cmd tea.Cmd + m.currentView, cmd = m.currentView.Update(msg) + return m, cmd } if m.err != nil { diff --git a/internal/app/edit_cell.go b/internal/app/edit_cell.go index 3f1b85d..db311f1 100644 --- a/internal/app/edit_cell.go +++ b/internal/app/edit_cell.go @@ -11,15 +11,16 @@ import ( ) type EditCellModel struct { - Shared *SharedData - rowIndex int - colIndex int - input textinput.Model - blinkState bool - keyMap EditCellKeyMap - help help.Model - focused bool - id int + Shared *SharedData + rowIndex int + colIndex int + input textinput.Model + blinkState bool + keyMap EditCellKeyMap + help help.Model + showFullHelp bool + focused bool + id int } // EditCellOption is a functional option for configuring EditCellModel @@ -106,6 +107,10 @@ func (m *EditCellModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { + case ToggleHelpMsg: + m.showFullHelp = !m.showFullHelp + return m, nil + case blinkMsg: m.blinkState = !m.blinkState cmds = append(cmds, blinkCmd()) @@ -146,7 +151,16 @@ func (m *EditCellModel) View() string { content := fmt.Sprintf("%s\n\n", TitleStyle.Render(fmt.Sprintf("Edit Cell: %s", columnName))) content += fmt.Sprintf("Value: %s\n\n", m.input.View()) - content += m.help.View(m.keyMap) + + var helpText string + if m.showFullHelp { + helpText = m.help.FullHelpView(m.keyMap.FullHelp()) + } else { + helpText = m.help.ShortHelpView(m.keyMap.ShortHelp()) + // Add ctrl+g to short help + helpText += " • " + HelpStyle.Render("ctrl+g: toggle help") + } + content += helpText return content } \ No newline at end of file diff --git a/internal/app/query.go b/internal/app/query.go index ff9572b..d990686 100644 --- a/internal/app/query.go +++ b/internal/app/query.go @@ -23,6 +23,7 @@ type QueryModel struct { gPressed bool keyMap QueryKeyMap help help.Model + showFullHelp bool focused bool id int } @@ -105,6 +106,10 @@ func (m *QueryModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { + case ToggleHelpMsg: + m.showFullHelp = !m.showFullHelp + return m, nil + case blinkMsg: m.blinkState = !m.blinkState cmds = append(cmds, tea.Tick(time.Millisecond*500, func(t time.Time) tea.Msg { @@ -156,17 +161,17 @@ func (m *QueryModel) handleResultsNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd case key.Matches(msg, m.keyMap.GoToStart): if m.gPressed { - // Second g - go to beginning + // Second g - go to beginning (gg pattern like vim) m.selectedRow = 0 m.gPressed = false } else { - // First g - wait for second g + // First g - wait for second g to complete gg sequence m.gPressed = true } return m, nil case key.Matches(msg, m.keyMap.GoToEnd): - // Go to end + // Go to end (G pattern like vim) if len(m.results) > 0 { m.selectedRow = len(m.results) - 1 } @@ -449,9 +454,17 @@ func (m *QueryModel) View() string { content.WriteString("\n") if m.FocusOnInput { - content.WriteString(HelpStyle.Render("enter: execute • esc: back")) + content.WriteString(HelpStyle.Render("enter: execute • esc: back • ctrl+g: toggle help")) } else { - content.WriteString(m.help.View(m.keyMap)) + var helpText string + if m.showFullHelp { + helpText = m.help.FullHelpView(m.keyMap.FullHelp()) + } else { + helpText = m.help.ShortHelpView(m.keyMap.ShortHelp()) + // Add ctrl+g to short help + helpText += " • " + HelpStyle.Render("ctrl+g: toggle help") + } + content.WriteString(helpText) } return content.String() diff --git a/internal/app/query_keys.go b/internal/app/query_keys.go index b161600..8679de2 100644 --- a/internal/app/query_keys.go +++ b/internal/app/query_keys.go @@ -2,7 +2,10 @@ package app import "github.com/charmbracelet/bubbles/key" -// QueryKeyMap defines keybindings for the query view +// QueryKeyMap defines keybindings for the query view. +// Navigation follows vim-like patterns: +// - gg: go to start (requires two 'g' presses) +// - G: go to end (single 'G' press) type QueryKeyMap struct { // Input mode keys Execute key.Binding @@ -100,7 +103,7 @@ func DefaultQueryKeyMap() QueryKeyMap { // ShortHelp returns keybindings to be shown in the mini help view func (k QueryKeyMap) ShortHelp() []key.Binding { - return []key.Binding{k.Execute, k.Up, k.Down, k.Enter} + return []key.Binding{k.Execute, k.Up, k.Down, k.GoToStart, k.GoToEnd, k.EditQuery} } // FullHelp returns keybindings for the expanded help view diff --git a/internal/app/row_detail.go b/internal/app/row_detail.go index ebe94d1..3b7c7bc 100644 --- a/internal/app/row_detail.go +++ b/internal/app/row_detail.go @@ -10,15 +10,16 @@ import ( ) type RowDetailModel struct { - Shared *SharedData - rowIndex int - selectedCol int - FromQuery bool - gPressed bool - keyMap RowDetailKeyMap - help help.Model - focused bool - id int + Shared *SharedData + rowIndex int + selectedCol int + FromQuery bool + gPressed bool + keyMap RowDetailKeyMap + help help.Model + showFullHelp bool + focused bool + id int } // RowDetailOption is a functional option for configuring RowDetailModel @@ -81,6 +82,10 @@ func (m *RowDetailModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } switch msg := msg.(type) { + case ToggleHelpMsg: + m.showFullHelp = !m.showFullHelp + return m, nil + case tea.KeyMsg: return m.handleNavigation(msg) } @@ -98,17 +103,17 @@ func (m *RowDetailModel) handleNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) { case key.Matches(msg, m.keyMap.GoToStart): if m.gPressed { - // Second g - go to beginning + // Second g - go to beginning (gg pattern like vim) m.selectedCol = 0 m.gPressed = false } else { - // First g - wait for second g + // First g - wait for second g to complete gg sequence m.gPressed = true } return m, nil case key.Matches(msg, m.keyMap.GoToEnd): - // Go to end + // Go to end (G pattern like vim) if len(m.Shared.Columns) > 0 { m.selectedCol = len(m.Shared.Columns) - 1 } @@ -176,7 +181,15 @@ func (m *RowDetailModel) View() string { } content.WriteString("\n") - content.WriteString(m.help.View(m.keyMap)) + var helpText string + if m.showFullHelp { + helpText = m.help.FullHelpView(m.keyMap.FullHelp()) + } else { + helpText = m.help.ShortHelpView(m.keyMap.ShortHelp()) + // Add ctrl+g to short help + helpText += " • " + HelpStyle.Render("ctrl+g: toggle help") + } + content.WriteString(helpText) return content.String() } \ No newline at end of file diff --git a/internal/app/row_detail_keys.go b/internal/app/row_detail_keys.go index 674caeb..6f1f36c 100644 --- a/internal/app/row_detail_keys.go +++ b/internal/app/row_detail_keys.go @@ -2,7 +2,10 @@ package app import "github.com/charmbracelet/bubbles/key" -// RowDetailKeyMap defines keybindings for the row detail view +// RowDetailKeyMap defines keybindings for the row detail view. +// Navigation follows vim-like patterns: +// - gg: go to start (requires two 'g' presses) +// - G: go to end (single 'G' press) type RowDetailKeyMap struct { Up key.Binding Down key.Binding @@ -49,7 +52,7 @@ func DefaultRowDetailKeyMap() RowDetailKeyMap { // ShortHelp returns keybindings to be shown in the mini help view func (k RowDetailKeyMap) ShortHelp() []key.Binding { - return []key.Binding{k.Up, k.Down, k.Enter, k.Back} + return []key.Binding{k.Up, k.Down, k.Enter, k.GoToStart, k.GoToEnd, k.Back} } // FullHelp returns keybindings for the expanded help view diff --git a/internal/app/table_data.go b/internal/app/table_data.go index 7019aa6..f6defab 100644 --- a/internal/app/table_data.go +++ b/internal/app/table_data.go @@ -12,15 +12,16 @@ import ( ) type TableDataModel struct { - Shared *SharedData - searchInput textinput.Model - searching bool - selectedRow int - gPressed bool - keyMap TableDataKeyMap - help help.Model - focused bool - id int + Shared *SharedData + searchInput textinput.Model + searching bool + selectedRow int + gPressed bool + keyMap TableDataKeyMap + help help.Model + showFullHelp bool + focused bool + id int } // TableDataOption is a functional option for configuring TableDataModel @@ -93,6 +94,10 @@ func (m *TableDataModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { + case ToggleHelpMsg: + m.showFullHelp = !m.showFullHelp + return m, nil + case tea.KeyMsg: if m.searching { return m.handleSearchInput(msg) @@ -151,20 +156,20 @@ func (m *TableDataModel) handleNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) { case key.Matches(msg, m.keyMap.GoToStart): if m.gPressed { - // Second g - go to absolute beginning + // Second g - go to absolute beginning (gg pattern like vim) m.Shared.CurrentPage = 0 m.Shared.LoadTableData() m.filterData() m.selectedRow = 0 m.gPressed = false } else { - // First g - wait for second g + // First g - wait for second g to complete gg sequence m.gPressed = true } return m, nil case key.Matches(msg, m.keyMap.GoToEnd): - // Go to absolute end + // Go to absolute end (G pattern like vim) maxPage := (m.Shared.TotalRows - 1) / PageSize m.Shared.CurrentPage = maxPage m.Shared.LoadTableData() @@ -440,7 +445,15 @@ func (m *TableDataModel) View() string { if m.searching { content.WriteString(HelpStyle.Render("Type to search • enter/esc: finish search")) } else { - content.WriteString(m.help.View(m.keyMap)) + var helpText string + if m.showFullHelp { + helpText = m.help.FullHelpView(m.keyMap.FullHelp()) + } else { + helpText = m.help.ShortHelpView(m.keyMap.ShortHelp()) + // Add ctrl+g to short help + helpText += " • " + HelpStyle.Render("ctrl+g: toggle help") + } + content.WriteString(helpText) } return content.String() diff --git a/internal/app/table_data_keys.go b/internal/app/table_data_keys.go index c6487d9..9b84121 100644 --- a/internal/app/table_data_keys.go +++ b/internal/app/table_data_keys.go @@ -2,7 +2,10 @@ package app import "github.com/charmbracelet/bubbles/key" -// TableDataKeyMap defines keybindings for the table data view +// TableDataKeyMap defines keybindings for the table data view. +// Navigation follows vim-like patterns: +// - gg: go to start (requires two 'g' presses) +// - G: go to end (single 'G' press) type TableDataKeyMap struct { Up key.Binding Down key.Binding @@ -74,7 +77,7 @@ func DefaultTableDataKeyMap() TableDataKeyMap { // ShortHelp returns keybindings to be shown in the mini help view func (k TableDataKeyMap) ShortHelp() []key.Binding { - return []key.Binding{k.Up, k.Down, k.Enter, k.Search} + return []key.Binding{k.Up, k.Down, k.Enter, k.GoToStart, k.GoToEnd, k.Search} } // FullHelp returns keybindings for the expanded help view diff --git a/internal/app/table_list.go b/internal/app/table_list.go index 129d505..e6f71c4 100644 --- a/internal/app/table_list.go +++ b/internal/app/table_list.go @@ -20,6 +20,7 @@ type TableListModel struct { gPressed bool keyMap TableListKeyMap help help.Model + showFullHelp bool focused bool id int } @@ -95,6 +96,10 @@ func (m *TableListModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { + case ToggleHelpMsg: + m.showFullHelp = !m.showFullHelp + return m, nil + case tea.KeyMsg: if m.searching { return m.handleSearchInput(msg) @@ -162,18 +167,18 @@ func (m *TableListModel) handleNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) { case key.Matches(msg, m.keyMap.GoToStart): if m.gPressed { - // Second g - go to beginning + // Second g - go to beginning (gg pattern like vim) m.selectedTable = 0 m.currentPage = 0 m.gPressed = false } else { - // First g - wait for second g + // First g - wait for second g to complete gg sequence m.gPressed = true } return m, nil case key.Matches(msg, m.keyMap.GoToEnd): - // Go to end + // Go to end (G pattern like vim) if len(m.Shared.FilteredTables) > 0 { m.selectedTable = len(m.Shared.FilteredTables) - 1 m.adjustPage() @@ -406,7 +411,15 @@ func (m *TableListModel) View() string { if m.searching { content.WriteString(HelpStyle.Render("Type to search • enter/esc: finish search")) } else { - content.WriteString(m.help.View(m.keyMap)) + var helpText string + if m.showFullHelp { + helpText = m.help.FullHelpView(m.keyMap.FullHelp()) + } else { + helpText = m.help.ShortHelpView(m.keyMap.ShortHelp()) + // Add ctrl+g to short help + helpText += " • " + HelpStyle.Render("ctrl+g: toggle help") + } + content.WriteString(helpText) } return content.String() diff --git a/internal/app/table_list_keys.go b/internal/app/table_list_keys.go index 83cfd9b..55a45ab 100644 --- a/internal/app/table_list_keys.go +++ b/internal/app/table_list_keys.go @@ -2,7 +2,10 @@ package app import "github.com/charmbracelet/bubbles/key" -// TableListKeyMap defines keybindings for the table list view +// TableListKeyMap defines keybindings for the table list view. +// Navigation follows vim-like patterns: +// - gg: go to start (requires two 'g' presses) +// - G: go to end (single 'G' press) type TableListKeyMap struct { Up key.Binding Down key.Binding @@ -69,7 +72,7 @@ func DefaultTableListKeyMap() TableListKeyMap { // ShortHelp returns keybindings to be shown in the mini help view func (k TableListKeyMap) ShortHelp() []key.Binding { - return []key.Binding{k.Up, k.Down, k.Enter, k.Search} + return []key.Binding{k.Up, k.Down, k.Enter, k.GoToStart, k.GoToEnd, k.Search} } // FullHelp returns keybindings for the expanded help view