jerry-rig a help toggle cmd

This commit is contained in:
2025-07-13 20:56:16 -07:00
parent 96b10e7b00
commit 5543a9a8e0
10 changed files with 147 additions and 55 deletions

View File

@@ -30,6 +30,7 @@ type blinkMsg struct{}
type AppKeyMap struct { type AppKeyMap struct {
Quit key.Binding Quit key.Binding
Suspend key.Binding Suspend key.Binding
ToggleHelp key.Binding
} }
// DefaultAppKeyMap returns the default keybindings // DefaultAppKeyMap returns the default keybindings
@@ -43,6 +44,10 @@ func DefaultAppKeyMap() AppKeyMap {
key.WithKeys("ctrl+z"), key.WithKeys("ctrl+z"),
key.WithHelp("ctrl+z", "suspend"), 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{} SwitchToQueryMsg struct{}
ReturnToQueryMsg struct{} // Return to query mode from row detail ReturnToQueryMsg struct{} // Return to query mode from row detail
RefreshDataMsg struct{} RefreshDataMsg struct{}
ToggleHelpMsg struct{} // Toggle between short and full help
UpdateCellMsg struct { UpdateCellMsg struct {
RowIndex, ColIndex int RowIndex, ColIndex int
Value string Value string
@@ -594,6 +600,8 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, tea.Quit return m, tea.Quit
case key.Matches(msg, m.keyMap.Suspend): case key.Matches(msg, m.keyMap.Suspend):
return m, tea.Suspend return m, tea.Suspend
case key.Matches(msg, m.keyMap.ToggleHelp):
return m, func() tea.Msg { return ToggleHelpMsg{} }
} }
case SwitchToTableListMsg: case SwitchToTableListMsg:
@@ -667,6 +675,12 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
queryModel.handleQueryCompletion(msg) queryModel.handleQueryCompletion(msg)
} }
return m, nil 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 { if m.err != nil {

View File

@@ -18,6 +18,7 @@ type EditCellModel struct {
blinkState bool blinkState bool
keyMap EditCellKeyMap keyMap EditCellKeyMap
help help.Model help help.Model
showFullHelp bool
focused bool focused bool
id int id int
} }
@@ -106,6 +107,10 @@ func (m *EditCellModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd var cmds []tea.Cmd
switch msg := msg.(type) { switch msg := msg.(type) {
case ToggleHelpMsg:
m.showFullHelp = !m.showFullHelp
return m, nil
case blinkMsg: case blinkMsg:
m.blinkState = !m.blinkState m.blinkState = !m.blinkState
cmds = append(cmds, blinkCmd()) 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("%s\n\n", TitleStyle.Render(fmt.Sprintf("Edit Cell: %s", columnName)))
content += fmt.Sprintf("Value: %s\n\n", m.input.View()) 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 return content
} }

View File

@@ -23,6 +23,7 @@ type QueryModel struct {
gPressed bool gPressed bool
keyMap QueryKeyMap keyMap QueryKeyMap
help help.Model help help.Model
showFullHelp bool
focused bool focused bool
id int id int
} }
@@ -105,6 +106,10 @@ func (m *QueryModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd var cmds []tea.Cmd
switch msg := msg.(type) { switch msg := msg.(type) {
case ToggleHelpMsg:
m.showFullHelp = !m.showFullHelp
return m, nil
case blinkMsg: case blinkMsg:
m.blinkState = !m.blinkState m.blinkState = !m.blinkState
cmds = append(cmds, tea.Tick(time.Millisecond*500, func(t time.Time) tea.Msg { 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): case key.Matches(msg, m.keyMap.GoToStart):
if m.gPressed { if m.gPressed {
// Second g - go to beginning // Second g - go to beginning (gg pattern like vim)
m.selectedRow = 0 m.selectedRow = 0
m.gPressed = false m.gPressed = false
} else { } else {
// First g - wait for second g // First g - wait for second g to complete gg sequence
m.gPressed = true m.gPressed = true
} }
return m, nil return m, nil
case key.Matches(msg, m.keyMap.GoToEnd): case key.Matches(msg, m.keyMap.GoToEnd):
// Go to end // Go to end (G pattern like vim)
if len(m.results) > 0 { if len(m.results) > 0 {
m.selectedRow = len(m.results) - 1 m.selectedRow = len(m.results) - 1
} }
@@ -449,9 +454,17 @@ func (m *QueryModel) View() string {
content.WriteString("\n") content.WriteString("\n")
if m.FocusOnInput { if m.FocusOnInput {
content.WriteString(HelpStyle.Render("enter: execute • esc: back")) content.WriteString(HelpStyle.Render("enter: execute • esc: back • ctrl+g: toggle help"))
} else { } 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() return content.String()

View File

@@ -2,7 +2,10 @@ package app
import "github.com/charmbracelet/bubbles/key" 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 { type QueryKeyMap struct {
// Input mode keys // Input mode keys
Execute key.Binding Execute key.Binding
@@ -100,7 +103,7 @@ func DefaultQueryKeyMap() QueryKeyMap {
// ShortHelp returns keybindings to be shown in the mini help view // ShortHelp returns keybindings to be shown in the mini help view
func (k QueryKeyMap) ShortHelp() []key.Binding { 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 // FullHelp returns keybindings for the expanded help view

View File

@@ -17,6 +17,7 @@ type RowDetailModel struct {
gPressed bool gPressed bool
keyMap RowDetailKeyMap keyMap RowDetailKeyMap
help help.Model help help.Model
showFullHelp bool
focused bool focused bool
id int id int
} }
@@ -81,6 +82,10 @@ func (m *RowDetailModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
switch msg := msg.(type) { switch msg := msg.(type) {
case ToggleHelpMsg:
m.showFullHelp = !m.showFullHelp
return m, nil
case tea.KeyMsg: case tea.KeyMsg:
return m.handleNavigation(msg) 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): case key.Matches(msg, m.keyMap.GoToStart):
if m.gPressed { if m.gPressed {
// Second g - go to beginning // Second g - go to beginning (gg pattern like vim)
m.selectedCol = 0 m.selectedCol = 0
m.gPressed = false m.gPressed = false
} else { } else {
// First g - wait for second g // First g - wait for second g to complete gg sequence
m.gPressed = true m.gPressed = true
} }
return m, nil return m, nil
case key.Matches(msg, m.keyMap.GoToEnd): case key.Matches(msg, m.keyMap.GoToEnd):
// Go to end // Go to end (G pattern like vim)
if len(m.Shared.Columns) > 0 { if len(m.Shared.Columns) > 0 {
m.selectedCol = len(m.Shared.Columns) - 1 m.selectedCol = len(m.Shared.Columns) - 1
} }
@@ -176,7 +181,15 @@ func (m *RowDetailModel) View() string {
} }
content.WriteString("\n") 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() return content.String()
} }

View File

@@ -2,7 +2,10 @@ package app
import "github.com/charmbracelet/bubbles/key" 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 { type RowDetailKeyMap struct {
Up key.Binding Up key.Binding
Down key.Binding Down key.Binding
@@ -49,7 +52,7 @@ func DefaultRowDetailKeyMap() RowDetailKeyMap {
// ShortHelp returns keybindings to be shown in the mini help view // ShortHelp returns keybindings to be shown in the mini help view
func (k RowDetailKeyMap) ShortHelp() []key.Binding { 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 // FullHelp returns keybindings for the expanded help view

View File

@@ -19,6 +19,7 @@ type TableDataModel struct {
gPressed bool gPressed bool
keyMap TableDataKeyMap keyMap TableDataKeyMap
help help.Model help help.Model
showFullHelp bool
focused bool focused bool
id int id int
} }
@@ -93,6 +94,10 @@ func (m *TableDataModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd var cmds []tea.Cmd
switch msg := msg.(type) { switch msg := msg.(type) {
case ToggleHelpMsg:
m.showFullHelp = !m.showFullHelp
return m, nil
case tea.KeyMsg: case tea.KeyMsg:
if m.searching { if m.searching {
return m.handleSearchInput(msg) 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): case key.Matches(msg, m.keyMap.GoToStart):
if m.gPressed { 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.CurrentPage = 0
m.Shared.LoadTableData() m.Shared.LoadTableData()
m.filterData() m.filterData()
m.selectedRow = 0 m.selectedRow = 0
m.gPressed = false m.gPressed = false
} else { } else {
// First g - wait for second g // First g - wait for second g to complete gg sequence
m.gPressed = true m.gPressed = true
} }
return m, nil return m, nil
case key.Matches(msg, m.keyMap.GoToEnd): 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 maxPage := (m.Shared.TotalRows - 1) / PageSize
m.Shared.CurrentPage = maxPage m.Shared.CurrentPage = maxPage
m.Shared.LoadTableData() m.Shared.LoadTableData()
@@ -440,7 +445,15 @@ func (m *TableDataModel) View() string {
if m.searching { if m.searching {
content.WriteString(HelpStyle.Render("Type to search • enter/esc: finish search")) content.WriteString(HelpStyle.Render("Type to search • enter/esc: finish search"))
} else { } 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() return content.String()

View File

@@ -2,7 +2,10 @@ package app
import "github.com/charmbracelet/bubbles/key" 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 { type TableDataKeyMap struct {
Up key.Binding Up key.Binding
Down key.Binding Down key.Binding
@@ -74,7 +77,7 @@ func DefaultTableDataKeyMap() TableDataKeyMap {
// ShortHelp returns keybindings to be shown in the mini help view // ShortHelp returns keybindings to be shown in the mini help view
func (k TableDataKeyMap) ShortHelp() []key.Binding { 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 // FullHelp returns keybindings for the expanded help view

View File

@@ -20,6 +20,7 @@ type TableListModel struct {
gPressed bool gPressed bool
keyMap TableListKeyMap keyMap TableListKeyMap
help help.Model help help.Model
showFullHelp bool
focused bool focused bool
id int id int
} }
@@ -95,6 +96,10 @@ func (m *TableListModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd var cmds []tea.Cmd
switch msg := msg.(type) { switch msg := msg.(type) {
case ToggleHelpMsg:
m.showFullHelp = !m.showFullHelp
return m, nil
case tea.KeyMsg: case tea.KeyMsg:
if m.searching { if m.searching {
return m.handleSearchInput(msg) 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): case key.Matches(msg, m.keyMap.GoToStart):
if m.gPressed { if m.gPressed {
// Second g - go to beginning // Second g - go to beginning (gg pattern like vim)
m.selectedTable = 0 m.selectedTable = 0
m.currentPage = 0 m.currentPage = 0
m.gPressed = false m.gPressed = false
} else { } else {
// First g - wait for second g // First g - wait for second g to complete gg sequence
m.gPressed = true m.gPressed = true
} }
return m, nil return m, nil
case key.Matches(msg, m.keyMap.GoToEnd): case key.Matches(msg, m.keyMap.GoToEnd):
// Go to end // Go to end (G pattern like vim)
if len(m.Shared.FilteredTables) > 0 { if len(m.Shared.FilteredTables) > 0 {
m.selectedTable = len(m.Shared.FilteredTables) - 1 m.selectedTable = len(m.Shared.FilteredTables) - 1
m.adjustPage() m.adjustPage()
@@ -406,7 +411,15 @@ func (m *TableListModel) View() string {
if m.searching { if m.searching {
content.WriteString(HelpStyle.Render("Type to search • enter/esc: finish search")) content.WriteString(HelpStyle.Render("Type to search • enter/esc: finish search"))
} else { } 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() return content.String()

View File

@@ -2,7 +2,10 @@ package app
import "github.com/charmbracelet/bubbles/key" 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 { type TableListKeyMap struct {
Up key.Binding Up key.Binding
Down key.Binding Down key.Binding
@@ -69,7 +72,7 @@ func DefaultTableListKeyMap() TableListKeyMap {
// ShortHelp returns keybindings to be shown in the mini help view // ShortHelp returns keybindings to be shown in the mini help view
func (k TableListKeyMap) ShortHelp() []key.Binding { 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 // FullHelp returns keybindings for the expanded help view