summaryrefslogtreecommitdiff
path: root/internal/tui/dashboard/model.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-09 22:35:53 +0200
committerPaul Buetow <paul@buetow.org>2026-03-09 22:35:53 +0200
commita4c72ad2cbe4ca857a5880675563b2ab4d24e1b5 (patch)
treed589398e9d02b760622716d9b2146c074eb4d66d /internal/tui/dashboard/model.go
parente40116ab0ecbbfd5cab5a33bc89e9bf6f98746aa (diff)
tui: add sortable syscalls dashboard table (task 363)
Diffstat (limited to 'internal/tui/dashboard/model.go')
-rw-r--r--internal/tui/dashboard/model.go77
1 files changed, 70 insertions, 7 deletions
diff --git a/internal/tui/dashboard/model.go b/internal/tui/dashboard/model.go
index 55661fb..7fea1c4 100644
--- a/internal/tui/dashboard/model.go
+++ b/internal/tui/dashboard/model.go
@@ -71,6 +71,7 @@ type Model struct {
pidFilter int
syscallsOffset int
syscallsCol int
+ syscallsSort tableSortState[syscallSortKey]
syscallsTreemapSelection int
filesOffset int
filesCol int
@@ -225,8 +226,12 @@ func (m Model) handleBubbleTick() (tea.Model, tea.Cmd) {
}
func (m Model) handleStatsTick(msg messages.StatsTickMsg) (tea.Model, tea.Cmd) {
+ selectedSyscall := ""
+ if m.syscallsSort.active {
+ selectedSyscall = m.selectedSyscallName()
+ }
m.latest = msg.Snap
- m.syscallsOffset = clampOffset(m.syscallsOffset, m.maxSyscallsRows())
+ m.reanchorSyscallsOffset(selectedSyscall)
m.syscallsTreemapSelection = clampOffset(m.syscallsTreemapSelection, m.maxSyscallsRows())
m.filesOffset = clampOffset(m.filesOffset, m.maxFilesRows())
m.filesDirOffset = clampOffset(m.filesDirOffset, m.maxFilesDirRowsForMode())
@@ -272,6 +277,9 @@ func (m Model) handleKey(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
handled, cmd = m.handleEnterKey(msg)
}
if !handled {
+ handled, cmd = m.handleSortKey(msg)
+ }
+ if !handled {
handled, cmd = m.handleShortcutKey(msg)
}
if !handled {
@@ -315,14 +323,10 @@ func (m Model) handleEnterKey(msg tea.KeyPressMsg) (bool, tea.Cmd) {
}
func (m Model) selectedSyscallFilter() (globalfilter.Filter, string, bool) {
- if m.latest == nil || m.syscallsOffset < 0 {
+ selected, ok := m.selectedSyscallSnapshot()
+ if !ok {
return globalfilter.Filter{}, "", false
}
- rows := m.latest.Syscalls()
- if len(rows) == 0 {
- return globalfilter.Filter{}, "", false
- }
- selected := rows[clampOffset(m.syscallsOffset, len(rows))]
if strings.TrimSpace(selected.Name) == "" {
return globalfilter.Filter{}, "", false
}
@@ -331,6 +335,62 @@ func (m Model) selectedSyscallFilter() (globalfilter.Filter, string, bool) {
return filter, "syscall~" + selected.Name, true
}
+func (m Model) selectedSyscallSnapshot() (statsengine.SyscallSnapshot, bool) {
+ rows := m.sortedSyscallRows()
+ if len(rows) == 0 {
+ return statsengine.SyscallSnapshot{}, false
+ }
+ index := clampOffset(m.syscallsOffset, len(rows))
+ return rows[index], true
+}
+
+func (m Model) sortedSyscallRows() []statsengine.SyscallSnapshot {
+ if m.latest == nil {
+ return nil
+ }
+ return sortedSyscallSnapshots(m.latest.Syscalls(), m.syscallsSort)
+}
+
+func (m Model) selectedSyscallName() string {
+ selected, ok := m.selectedSyscallSnapshot()
+ if !ok {
+ return ""
+ }
+ return selected.Name
+}
+
+func (m *Model) handleSortKey(msg tea.KeyPressMsg) (bool, tea.Cmd) {
+ if !key.Matches(msg, m.keys.Sort) {
+ return false, nil
+ }
+ if m.activeTab != TabSyscalls || m.syscallsVizMode != tabVizModeTable {
+ return false, nil
+ }
+ key, ok := syscallSortKeyForColumn(m.width, m.syscallsCol)
+ if !ok {
+ return false, nil
+ }
+ selectedName := m.selectedSyscallName()
+ m.syscallsSort = m.syscallsSort.toggled(key)
+ m.reanchorSyscallsOffset(selectedName)
+ return true, nil
+}
+
+func (m *Model) reanchorSyscallsOffset(selectedName string) {
+ rows := m.sortedSyscallRows()
+ if len(rows) == 0 {
+ m.syscallsOffset = 0
+ return
+ }
+ if selectedName != "" {
+ if index, ok := findSyscallOffset(rows, selectedName); ok {
+ m.syscallsOffset = index
+ return
+ }
+ }
+ m.syscallsOffset = clampOffset(m.syscallsOffset, len(rows))
+}
+
func (m Model) selectedFileFilter() (globalfilter.Filter, string, bool) {
if m.latest == nil {
return globalfilter.Filter{}, "", false
@@ -854,6 +914,9 @@ func (m Model) renderActiveContent(width, activeHeight int, streamModel *eventst
return chart.Render("Processes", width, activeHeight)
}
}
+ if m.activeTab == TabSyscalls && m.latest != nil {
+ return renderSyscallsWithSort(m.latest, width, activeHeight, m.syscallsOffset, m.syscallsCol, m.syscallsSort)
+ }
return renderActiveTab(
m.activeTab,
m.latest,