tweaks to controls for pagination

This commit is contained in:
2025-07-13 20:06:40 -07:00
parent 2fa8ebe741
commit 1d9c8fff73
4 changed files with 174 additions and 6 deletions

View File

@@ -19,6 +19,7 @@ type QueryModel struct {
columns []string
err error
blinkState bool
gPressed bool
}
func NewQueryModel(shared *SharedData) *QueryModel {
@@ -106,13 +107,35 @@ func (m *QueryModel) handleQueryInput(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
func (m *QueryModel) handleResultsNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
switch msg.String() {
case "esc", "q":
m.gPressed = false
return m, func() tea.Msg { return SwitchToTableListMsg{} }
case "g":
if m.gPressed {
// Second g - go to beginning
m.selectedRow = 0
m.gPressed = false
} else {
// First g - wait for second g
m.gPressed = true
}
return m, nil
case "G":
// Go to end
if len(m.results) > 0 {
m.selectedRow = len(m.results) - 1
}
m.gPressed = false
return m, nil
case "i":
m.gPressed = false
m.FocusOnInput = true
return m, nil
case "enter":
m.gPressed = false
if len(m.results) > 0 {
return m, func() tea.Msg {
return SwitchToRowDetailFromQueryMsg{RowIndex: m.selectedRow}
@@ -120,14 +143,20 @@ func (m *QueryModel) handleResultsNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd
}
case "up", "k":
m.gPressed = false
if m.selectedRow > 0 {
m.selectedRow--
}
case "down", "j":
m.gPressed = false
if m.selectedRow < len(m.results)-1 {
m.selectedRow++
}
default:
// Any other key resets the g state
m.gPressed = false
}
return m, nil
}
@@ -460,7 +489,7 @@ func (m *QueryModel) View() string {
if m.FocusOnInput {
content.WriteString(HelpStyle.Render("enter: execute • esc: back • ctrl+w: delete word • ctrl+arrows: word nav"))
} else {
content.WriteString(HelpStyle.Render("↑/↓: navigate • enter: details • i: edit query • q: back"))
content.WriteString(HelpStyle.Render("↑/↓: navigate • enter: details • i: edit query • gg/G: first/last • q: back"))
}
return content.String()

View File

@@ -12,6 +12,7 @@ type RowDetailModel struct {
rowIndex int
selectedCol int
FromQuery bool
gPressed bool
}
func NewRowDetailModel(shared *SharedData, rowIndex int) *RowDetailModel {
@@ -31,12 +32,33 @@ func (m *RowDetailModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyMsg:
switch msg.String() {
case "q", "esc":
m.gPressed = false
if m.FromQuery {
return m, func() tea.Msg { return ReturnToQueryMsg{} }
}
return m, func() tea.Msg { return SwitchToTableDataMsg{TableIndex: m.Shared.SelectedTable} }
case "g":
if m.gPressed {
// Second g - go to beginning
m.selectedCol = 0
m.gPressed = false
} else {
// First g - wait for second g
m.gPressed = true
}
return m, nil
case "G":
// Go to end
if len(m.Shared.Columns) > 0 {
m.selectedCol = len(m.Shared.Columns) - 1
}
m.gPressed = false
return m, nil
case "e":
m.gPressed = false
if len(m.Shared.FilteredData) > m.rowIndex && len(m.Shared.Columns) > m.selectedCol {
return m, func() tea.Msg {
return SwitchToEditCellMsg{RowIndex: m.rowIndex, ColIndex: m.selectedCol}
@@ -44,14 +66,20 @@ func (m *RowDetailModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
case "up", "k":
m.gPressed = false
if m.selectedCol > 0 {
m.selectedCol--
}
case "down", "j":
m.gPressed = false
if m.selectedCol < len(m.Shared.Columns)-1 {
m.selectedCol++
}
default:
// Any other key resets the g state
m.gPressed = false
}
}
return m, nil
@@ -84,7 +112,7 @@ func (m *RowDetailModel) View() string {
}
content.WriteString("\n")
content.WriteString(HelpStyle.Render("↑/↓: navigate columns • e: edit • q: back"))
content.WriteString(HelpStyle.Render("↑/↓: navigate columns • e: edit • gg/G: first/last • q: back"))
return content.String()
}

View File

@@ -12,6 +12,7 @@ type TableDataModel struct {
selectedRow int
searchInput string
searching bool
gPressed bool
}
func NewTableDataModel(shared *SharedData) *TableDataModel {
@@ -58,9 +59,11 @@ func (m *TableDataModel) handleSearchInput(msg tea.KeyMsg) (tea.Model, tea.Cmd)
func (m *TableDataModel) handleNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
switch msg.String() {
case "q":
m.gPressed = false
return m, func() tea.Msg { return SwitchToTableListMsg{} }
case "esc":
m.gPressed = false
if m.searchInput != "" {
// Clear search filter
m.searchInput = ""
@@ -69,7 +72,32 @@ func (m *TableDataModel) handleNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
}
return m, func() tea.Msg { return SwitchToTableListMsg{} }
case "g":
if m.gPressed {
// Second g - go to absolute beginning
m.Shared.CurrentPage = 0
m.Shared.LoadTableData()
m.filterData()
m.selectedRow = 0
m.gPressed = false
} else {
// First g - wait for second g
m.gPressed = true
}
return m, nil
case "G":
// Go to absolute end
maxPage := (m.Shared.TotalRows - 1) / PageSize
m.Shared.CurrentPage = maxPage
m.Shared.LoadTableData()
m.filterData()
m.selectedRow = len(m.Shared.FilteredData) - 1
m.gPressed = false
return m, nil
case "enter":
m.gPressed = false
if len(m.Shared.FilteredData) > 0 {
return m, func() tea.Msg {
return SwitchToRowDetailMsg{RowIndex: m.selectedRow}
@@ -77,29 +105,50 @@ func (m *TableDataModel) handleNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
}
case "/":
m.gPressed = false
m.searching = true
m.searchInput = ""
return m, nil
case "s":
m.gPressed = false
return m, func() tea.Msg { return SwitchToQueryMsg{} }
case "r":
m.gPressed = false
if err := m.Shared.LoadTableData(); err == nil {
m.filterData()
}
case "up", "k":
m.gPressed = false
if m.selectedRow > 0 {
m.selectedRow--
} else if m.Shared.CurrentPage > 0 {
// At top of current page, go to previous page
m.Shared.CurrentPage--
m.Shared.LoadTableData()
m.filterData()
m.selectedRow = len(m.Shared.FilteredData) - 1 // Go to last row of previous page
}
case "down", "j":
m.gPressed = false
if m.selectedRow < len(m.Shared.FilteredData)-1 {
m.selectedRow++
} else {
// At bottom of current page, try to go to next page
maxPage := (m.Shared.TotalRows - 1) / PageSize
if m.Shared.CurrentPage < maxPage {
m.Shared.CurrentPage++
m.Shared.LoadTableData()
m.filterData()
m.selectedRow = 0 // Go to first row of next page
}
}
case "left", "h":
m.gPressed = false
if m.Shared.CurrentPage > 0 {
m.Shared.CurrentPage--
m.Shared.LoadTableData()
@@ -107,12 +156,17 @@ func (m *TableDataModel) handleNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
}
case "right", "l":
m.gPressed = false
maxPage := (m.Shared.TotalRows - 1) / PageSize
if m.Shared.CurrentPage < maxPage {
m.Shared.CurrentPage++
m.Shared.LoadTableData()
m.selectedRow = 0
}
default:
// Any other key resets the g state
m.gPressed = false
}
return m, nil
}
@@ -178,10 +232,21 @@ func (m *TableDataModel) View() string {
content.WriteString(TitleStyle.Render(headerRow))
content.WriteString("\n")
// Show data rows
// Show data rows with scrolling within current page
visibleCount := Max(1, m.Shared.Height-10)
totalRows := len(m.Shared.FilteredData)
startIdx := 0
endIdx := Min(len(m.Shared.FilteredData), visibleCount)
// If there are more rows than can fit on screen, scroll the view
if totalRows > visibleCount && m.selectedRow >= visibleCount {
startIdx = m.selectedRow - visibleCount + 1
// Ensure we don't scroll past the end
if startIdx > totalRows-visibleCount {
startIdx = totalRows - visibleCount
}
}
endIdx := Min(totalRows, startIdx+visibleCount)
for i := startIdx; i < endIdx; i++ {
row := m.Shared.FilteredData[i]
@@ -206,7 +271,7 @@ func (m *TableDataModel) View() string {
if m.searching {
content.WriteString(HelpStyle.Render("Type to search • enter/esc: finish search"))
} else {
content.WriteString(HelpStyle.Render("↑/↓: navigate • ←/→: page • /: search • enter: details • s: SQL • r: refresh • q: back"))
content.WriteString(HelpStyle.Render("↑/↓: navigate • ←/→: page • /: search • enter: details • s: SQL • r: refresh • gg/G: first/last • q: back"))
}
return content.String()

View File

@@ -13,6 +13,7 @@ type TableListModel struct {
searching bool
selectedTable int
currentPage int
gPressed bool
}
func NewTableListModel(shared *SharedData) *TableListModel {
@@ -59,12 +60,47 @@ func (m *TableListModel) handleSearchInput(msg tea.KeyMsg) (tea.Model, tea.Cmd)
func (m *TableListModel) handleNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
switch msg.String() {
case "esc":
if m.searchInput != "" {
// Clear search filter
m.searchInput = ""
m.filterTables()
m.gPressed = false
return m, nil
}
// If no filter, escape does nothing (could exit app but that's handled at higher level)
m.gPressed = false
return m, nil
case "/":
m.searching = true
m.searchInput = ""
m.gPressed = false
return m, nil
case "g":
if m.gPressed {
// Second g - go to beginning
m.selectedTable = 0
m.currentPage = 0
m.gPressed = false
} else {
// First g - wait for second g
m.gPressed = true
}
return m, nil
case "G":
// Go to end
if len(m.Shared.FilteredTables) > 0 {
m.selectedTable = len(m.Shared.FilteredTables) - 1
m.adjustPage()
}
m.gPressed = false
return m, nil
case "enter":
m.gPressed = false
if len(m.Shared.FilteredTables) > 0 {
return m, func() tea.Msg {
return SwitchToTableDataMsg{TableIndex: m.selectedTable}
@@ -72,32 +108,38 @@ func (m *TableListModel) handleNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
}
case "s":
m.gPressed = false
return m, func() tea.Msg { return SwitchToQueryMsg{} }
case "r":
m.gPressed = false
if err := m.Shared.LoadTables(); err == nil {
m.filterTables()
}
case "up", "k":
m.gPressed = false
if m.selectedTable > 0 {
m.selectedTable--
m.adjustPage()
}
case "down", "j":
m.gPressed = false
if m.selectedTable < len(m.Shared.FilteredTables)-1 {
m.selectedTable++
m.adjustPage()
}
case "left", "h":
m.gPressed = false
if m.currentPage > 0 {
m.currentPage--
m.selectedTable = m.currentPage * m.getVisibleCount()
}
case "right", "l":
m.gPressed = false
maxPage := (len(m.Shared.FilteredTables) - 1) / m.getVisibleCount()
if m.currentPage < maxPage {
m.currentPage++
@@ -106,6 +148,10 @@ func (m *TableListModel) handleNavigation(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
m.selectedTable = len(m.Shared.FilteredTables) - 1
}
}
default:
// Any other key resets the g state
m.gPressed = false
}
return m, nil
}
@@ -190,7 +236,7 @@ func (m *TableListModel) View() string {
if m.searching {
content.WriteString(HelpStyle.Render("Type to search • enter/esc: finish search"))
} else {
content.WriteString(HelpStyle.Render("↑/↓: navigate • ←/→: page • /: search • enter: view • s: SQL • r: refresh • ctrl+c: quit"))
content.WriteString(HelpStyle.Render("↑/↓: navigate • ←/→: page • /: search • enter: view • s: SQL • r: refresh • gg/G: first/last • ctrl+c: quit"))
}
return content.String()