fix: Implement proper scrolling and sorting for Analysis calls list

This commit is contained in:
Jose Luis Montañes Ojados
2026-01-19 23:07:25 +01:00
parent d234bfb427
commit a5ba4d6c11
2 changed files with 95 additions and 7 deletions

View File

@@ -153,6 +153,27 @@ func (s *CallFlowStore) GetRecentFlows(n int) []*CallFlow {
return flows
}
// GetSortedFlows returns all call flows sorted by StartTime (oldest first)
func (s *CallFlowStore) GetSortedFlows() []*CallFlow {
flows := s.GetAllFlows()
// Sort by start time ascending (oldest first), then by CallID for stable order
for i := 0; i < len(flows)-1; i++ {
for j := i + 1; j < len(flows); j++ {
// Compare by StartTime first
if flows[i].StartTime.After(flows[j].StartTime) {
flows[i], flows[j] = flows[j], flows[i]
} else if flows[i].StartTime.Equal(flows[j].StartTime) {
// If same time, sort by CallID for stable order
if flows[i].CallID > flows[j].CallID {
flows[i], flows[j] = flows[j], flows[i]
}
}
}
}
return flows
}
// Count returns the number of call flows
func (s *CallFlowStore) Count() int {
s.mu.RLock()

View File

@@ -79,6 +79,7 @@ type Model struct {
callFlowStore *sip.CallFlowStore
// Call flow analysis
analysisOffset int // For pagination in analysis view
selectedFlow int
selectedPacketIndex int
flowList list.Model
@@ -484,11 +485,51 @@ func (m *Model) handleViewKeys(msg tea.KeyMsg) tea.Cmd {
case "up", "k":
if m.selectedFlow > 0 {
m.selectedFlow--
if m.selectedFlow < m.analysisOffset {
m.analysisOffset = m.selectedFlow
}
}
case "down", "j":
flows := m.callFlowStore.GetRecentFlows(20)
flows := m.callFlowStore.GetSortedFlows()
if m.selectedFlow < len(flows)-1 {
m.selectedFlow++
// Check if we need to scroll down
// Visible height calculation (approximate)
headerHeight := 4 // Title + Call count + padding
footerHeight := 2 // Help text
availableHeight := m.height - headerHeight - footerHeight
if availableHeight < 5 {
availableHeight = 5
}
if m.selectedFlow >= m.analysisOffset+availableHeight {
m.analysisOffset++
}
}
case "pgup":
m.selectedFlow -= 10
if m.selectedFlow < 0 {
m.selectedFlow = 0
}
if m.selectedFlow < m.analysisOffset {
m.analysisOffset = m.selectedFlow
}
case "pgdown":
flows := m.callFlowStore.GetSortedFlows()
m.selectedFlow += 10
if m.selectedFlow >= len(flows) {
m.selectedFlow = len(flows) - 1
}
headerHeight := 4
footerHeight := 2
availableHeight := m.height - headerHeight - footerHeight
if availableHeight < 5 {
availableHeight = 5
}
if m.selectedFlow >= m.analysisOffset+availableHeight {
m.analysisOffset = m.selectedFlow - availableHeight + 1
}
case "enter":
m.subView = SubViewCallDetail
@@ -1209,7 +1250,7 @@ func (m Model) viewCapture() string {
func (m Model) viewAnalysis() string {
title := m.styles.Title.Render("📊 Analysis")
flows := m.callFlowStore.GetRecentFlows(20)
flows := m.callFlowStore.GetSortedFlows()
if len(flows) == 0 {
return lipgloss.JoinVertical(lipgloss.Left, title,
@@ -1221,8 +1262,9 @@ func (m Model) viewAnalysis() string {
}
var lines []string
lines = append(lines, fmt.Sprintf("Calls: %d", len(flows)))
lines = append(lines, "")
// Header is handled in return statement now
// lines = append(lines, fmt.Sprintf("Calls: %d", len(flows)))
// lines = append(lines, "")
for i, flow := range flows {
prefix := " "
@@ -1253,10 +1295,35 @@ func (m Model) viewAnalysis() string {
lines = append(lines, style.Render(summary))
}
lines = append(lines, "")
lines = append(lines, m.styles.Help.Render("↑/↓ select • Enter details • q quit"))
// Apply Viewport/Scrolling
headerHeight := 4 // Title + Call count + padding
footerHeight := 2 // Help text
availableHeight := m.height - headerHeight - footerHeight
if availableHeight < 5 {
availableHeight = 5
}
return lipgloss.JoinVertical(lipgloss.Left, title, lipgloss.JoinVertical(lipgloss.Left, lines...))
// Sanity check offset
if m.analysisOffset > len(lines)-1 {
m.analysisOffset = len(lines) - 1
}
if m.analysisOffset < 0 {
m.analysisOffset = 0
}
end := m.analysisOffset + availableHeight
if end > len(lines) {
end = len(lines)
}
visibleLines := lines[m.analysisOffset:end]
return lipgloss.JoinVertical(lipgloss.Left, title,
fmt.Sprintf("Calls: %d (Showing %d-%d)", len(flows), m.analysisOffset+1, end),
"",
lipgloss.JoinVertical(lipgloss.Left, visibleLines...),
"",
m.styles.Help.Render("↑/↓ select • Enter details • q quit"))
}
func (m Model) viewNetworkMap() string {