diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-18 20:54:35 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-18 20:54:35 +0200 |
| commit | cd554b0af706b5f62b4e1bfde04091052b4aac61 (patch) | |
| tree | e6d02f1c2a1da27da17386e8832c2d4a3e699cdf /docs/tui-dashboard-table-sorting-plan.md | |
| parent | b421b2232351049277ee4ad5b31367bb2b6779bb (diff) | |
cleanup
Diffstat (limited to 'docs/tui-dashboard-table-sorting-plan.md')
| -rw-r--r-- | docs/tui-dashboard-table-sorting-plan.md | 336 |
1 files changed, 0 insertions, 336 deletions
diff --git a/docs/tui-dashboard-table-sorting-plan.md b/docs/tui-dashboard-table-sorting-plan.md deleted file mode 100644 index 0d4586e..0000000 --- a/docs/tui-dashboard-table-sorting-plan.md +++ /dev/null @@ -1,336 +0,0 @@ -# TUI Dashboard Table Sorting Plan - -## Overview - -Add column-driven sorting to the dashboard table views for: - -- `3:Syscalls` -- `4:Files` -- `5:Processes` - -This is a **table-view-only** feature. Bubble, treemap, and icicle modes keep -their existing ordering rules. - -The task wording says "sort by any row", but the current dashboard already -tracks both a selected row and a selected column. This plan therefore treats -`s` as **sort by the currently selected column/cell**. - -Pressing `s`: - -1. on a new selected column enables that column's sort order -2. again on the same selected column clears the custom sort and restores the - tab's current default ordering - -## Current Behavior - -The dashboard already has the key pieces needed for this feature: - -- `internal/tui/dashboard/model.go` - - stores row selection and selected column for Syscalls, Files, and Processes - - routes table navigation with `left/right` and `h/l` -- `internal/tui/dashboard/syscalls.go` - - renders the syscall table from `snap.Syscalls()` -- `internal/tui/dashboard/files.go` - - renders both the file table and the grouped-directory table -- `internal/tui/dashboard/processes.go` - - renders the process table - -The current default ordering comes from the snapshot producers: - -- Syscalls: `Count desc`, then `Name asc` -- Files: `Accesses desc`, then `Path asc` -- Grouped directories: `Accesses desc`, then `Directory asc` -- Processes: `Syscalls desc`, then `Bytes desc`, then `PID asc` - -That ordering should remain the baseline whenever no custom sort is active. - -## Design Goals - -- `s` sorts by the selected column in table mode. -- `s` on the same selected column toggles back to the default ranking. -- `Enter` continues to act on the row currently visible on screen after sorting. -- Sorting stays in the dashboard layer; `statsengine` snapshot semantics do not - change. -- Selection remains anchored to the same logical entity when sorting changes. -- Width changes do not corrupt sort state for the Syscalls tab. - -## UX Rules - -- `s` is active only for sortable dashboard tables: - - Syscalls table mode - - Files table mode - - Files directory-grouped table mode - - Processes table mode -- `s` does nothing in: - - Overview - - Latency+Gaps - - Stream - - Flame - - bubble/treemap/icicle modes -- Table footer hints should add `s:sort`. -- The footer should also show the active sort, for example: - - `sort: default` - - `sort: p95 desc` - - `sort: Path asc` -- Expanded help should mention `s` so the feature is discoverable. - -## State Model - -Add dashboard-local sort state per table shape. - -Example shape: - -```go -type tableSortState[K comparable] struct { - active bool - key K -} -``` - -Recommended fields on `dashboard.Model`: - -- `syscallsSort` -- `filesSort` -- `filesDirSort` -- `processesSort` - -`Files` needs **two** sort states because the tab has two different table -schemas: - -- file rows -- grouped directory rows - -Those states should persist independently when `d` toggles between files and -directories. - -## Logical Sort Keys - -Do **not** store the raw selected column index as the sort identifier. - -The Syscalls table changes shape by width: - -- narrow layout: `Syscall Count Rate/s Avg p95 p99 Bytes Errors` -- wide layout: `Syscall Count Rate/s Avg Min Max p50 p95 p99 Bytes Errors` - -If sort state stored only a column index, resizing from narrow to wide would -turn "sort by p95" into "sort by Min". The sort state must therefore use a -stable logical key enum, and map the current visible column index to that enum -at keypress time. - -Recommended enums: - -- `syscallSortKey` -- `fileSortKey` -- `fileDirSortKey` -- `processSortKey` - -## Column Ordering Rules - -Use a fixed natural direction per logical column. This avoids inventing a -three-state cycle and matches the task requirement of "sort" plus "toggle back". - -### Syscalls - -- `Syscall`: `Name asc` -- `Count`: `Count desc` -- `Rate/s`: `RatePerSec desc` -- `Avg`: `LatencyMeanNs desc` -- `Min`: `LatencyMinNs desc` -- `Max`: `LatencyMaxNs desc` -- `p50`: `LatencyP50Ns desc` -- `p95`: `LatencyP95Ns desc` -- `p99`: `LatencyP99Ns desc` -- `Bytes`: `Bytes desc` -- `Errors`: `Errors desc` - -### Files - -- `Accesses`: `Accesses desc` -- `Read`: `BytesRead desc` -- `Write`: `BytesWritten desc` -- `Avg Latency`: `AvgLatencyNs desc` -- `Max Latency`: `MaxLatencyNs desc` -- `Path`: `Path asc` - -### Grouped Directories - -- `Accesses`: `Accesses desc` -- `Read`: `BytesRead desc` -- `Write`: `BytesWritten desc` -- `Avg Latency`: `AvgLatencyNs desc` -- `Max Latency`: `MaxLatencyNs desc` -- `Files`: `FileCount desc` -- `Directory`: `Dir asc` - -### Processes - -- `PID`: `PID asc` -- `Comm`: `Comm asc` -- `Syscalls`: `Syscalls desc` -- `Rate/s`: `RatePerSec desc` -- `Total Bytes`: `Bytes desc` -- `Avg Latency`: `AvgLatencyNs desc` - -## Comparator Rules - -For deterministic output, custom comparators should fall back to the existing -default ranking for that row type. - -Examples: - -- `p95 desc`, then syscall default order -- `Path asc`, then file default order -- `Comm asc`, then process default order - -This keeps ties stable and makes the "toggle back to default" behavior -predictable. - -## Selection Anchoring - -Changing sort order must not leave the cursor on the same numeric row index if -that index now points to a different entity. - -Before toggling sort: - -1. capture the currently selected logical entity key -2. recompute the sorted rows -3. restore the selected row to the same entity in the new order -4. if the entity no longer exists, clamp as today - -Recommended identity keys: - -- Syscalls: `Name` -- Files: `Path` -- Grouped directories: `Dir` -- Processes: `PID` - -This same anchor logic should run on refresh ticks while custom sorting is -active so the selected item does not drift unpredictably as live stats change. - -## Implementation Shape - -Keep the sorting logic in `internal/tui/dashboard`, not in `internal/statsengine`. - -Reason: - -- snapshot order is part of the existing aggregate ranking behavior -- only the table presentation needs alternate ordering -- bubble/treemap/icicle already have their own ordering rules - -Recommended implementation split: - -- `internal/tui/common/keys.go` - - add a `Sort` binding for `s` - - include it in dashboard help output -- `internal/tui/dashboard/model.go` - - add per-table sort state - - handle `s` - - ignore `s` outside sortable table modes - - preserve selection anchors when sort changes - - make `selectedSyscallFilter`, `selectedFileFilter`, and - `selectedProcessSnapshot` read from the same sorted rows used by rendering -- `internal/tui/dashboard/syscalls.go` - - add syscall sort key mapping from visible column index - - add sorted syscall row helper - - expose active sort label for footer hints -- `internal/tui/dashboard/files.go` - - add file and directory sort key helpers - - keep file and grouped-directory comparators separate -- `internal/tui/dashboard/processes.go` - - add process sort key helpers and sorted row helper -- `internal/tui/dashboard/table.go` - - extend footer hints/status rendering as needed for the active sort label - -## Rendering/Data Consistency - -The most important implementation rule is: - -**the rendered rows and the row-selection actions must use the exact same sorted -slice** - -Without this, the UI can show one row while `Enter` filters a different row. - -The safest approach is to centralize each table's sorted typed rows in helper -functions and use those helpers in both: - -- render paths -- selected-row action paths - -## Files Tab Details - -The Files tab needs one extra rule beyond Syscalls and Processes: - -- in plain file mode, sorting operates on `[]statsengine.FileSnapshot` -- in directory-grouped mode, sorting operates on `[]DirSnapshot` - -The two modes should not share a single sort key because their columns differ. -Switching with `d` should preserve: - -- last file-table custom sort -- last directory-table custom sort - -## Interaction With Existing Features - -- `Enter` - - still filters the currently selected visible row -- `d` - - only changes Files table shape; custom sort state persists per mode -- `v` - - custom sort state persists, but only applies when returning to table mode -- `b` - - unaffected; bubble/treemap ordering remains metric-driven -- terminal resize - - sort state persists because it stores logical keys, not raw indices -- trace restart / filter apply - - sort state should remain as view state - -## Testing Plan - -Add focused tests in `internal/tui/dashboard` and `internal/tui/common`. - -### Model behavior - -- `s` on Syscalls enables a column sort. -- `s` on the same Syscalls column restores default sorting. -- `s` on Processes does nothing in non-table modes. -- `s` on Files uses file-mode sort state when `filesDirGrouped == false`. -- `s` on Files uses directory-mode sort state when `filesDirGrouped == true`. -- changing sort preserves the selected entity instead of only the row index. - -### Width-sensitive syscall behavior - -- sorting by `p95` in narrow mode survives a resize into wide mode -- sorting by `Syscall` or `Count` maps correctly in both layouts - -### Selection action consistency - -- `selectedSyscallFilter()` uses sorted syscall rows -- `selectedFileFilter()` uses sorted file or directory rows -- `selectedProcessSnapshot()` uses sorted process rows in table mode - -### Help/footer rendering - -- expanded help includes `s` -- table footer includes `s:sort` -- active sort label is visible in the table footer - -### Negative cases - -- `s` does nothing on Overview / Stream / Flame / Latency+Gaps -- `s` does nothing for bubble / treemap / icicle views - -## Recommended Delivery Order - -1. add key binding and sort state plumbing in `dashboard.Model` -2. implement sorted typed-row helpers per tab -3. switch render paths and selected-row actions to the shared helpers -4. add footer/help output -5. add regression tests for sort toggling, width changes, and selected-row - action consistency - -## Non-Goals - -- no change to snapshot generation order in `statsengine` -- no sortable Overview or Latency+Gaps tables -- no ascending/descending toggle cycle beyond "custom sort" vs "default" -- no behavior change for bubble/treemap/icicle ordering |
