@@ -27,12 +27,11 @@ type endpointID struct {
2727type EndpointsCard struct {
2828 endpoints []endpoint
2929 sorted []endpoint
30- selectMode bool
30+ selectMode bool
3131 selectedIndex int
32+ scrollOffset int
3233}
3334
34- const maxEndpoints = 35 // Maximum number of endpoints to display
35-
3635func NewEndpointsCard (logs []nginx.NGINXLog , period period.Period ) * EndpointsCard {
3736 card := & EndpointsCard {}
3837 card .UpdateCalculated (logs , period )
@@ -67,6 +66,21 @@ func (p *EndpointsCard) RenderContent(width, height int) string {
6766 }
6867
6968 endpoints := p .sorted
69+ if p .selectMode {
70+ p .ensureSelectionVisible (height )
71+ } else {
72+ p .scrollOffset = 0
73+ }
74+
75+ start := p .scrollOffset
76+ if start < 0 {
77+ start = 0
78+ }
79+ if start > len (endpoints ) {
80+ start = len (endpoints )
81+ }
82+ end := min (start + height , len (endpoints ))
83+ visibleEndpoints := endpoints [start :end ]
7084
7185 // Find max count for scaling bars
7286 maxCount := endpoints [0 ].count
@@ -89,16 +103,13 @@ func (p *EndpointsCard) RenderContent(width, height int) string {
89103 var buf strings.Builder
90104
91105 // Render each endpoint as a horizontal bar with overlaid text
92- for i , ep := range endpoints {
93- if i >= height {
94- break // Don't exceed available height
95- }
96-
106+ for i , ep := range visibleEndpoints {
97107 if i > 0 {
98108 buf .WriteByte ('\n' )
99109 }
100110
101- isSelected := p .selectMode && i == p .selectedIndex
111+ actualIndex := start + i
112+ isSelected := p .selectMode && actualIndex == p .selectedIndex
102113
103114 // Calculate bar length proportional to count, using full width
104115 barLength := 0
@@ -192,7 +203,7 @@ func (p *EndpointsCard) RenderContent(width, height int) string {
192203 }
193204
194205 // Fill remaining height with empty lines
195- for i := min ( len (endpoints ), height ); i < height ; i ++ {
206+ for i := len (visibleEndpoints ); i < height ; i ++ {
196207 buf .WriteByte ('\n' )
197208 }
198209
@@ -219,9 +230,6 @@ func (r *EndpointsCard) sortEndpoints() []endpoint {
219230 }
220231 return sorted [i ].status < sorted [j ].status
221232 })
222- if len (sorted ) > maxEndpoints {
223- sorted = sorted [:maxEndpoints ]
224- }
225233 return sorted
226234}
227235
@@ -262,10 +270,12 @@ func (r *EndpointsCard) GetRequiredHeight(width int) int {
262270func (r * EndpointsCard ) EnterSelectMode () {
263271 r .selectMode = true
264272 r .selectedIndex = 0
273+ r .scrollOffset = 0
265274}
266275
267276func (r * EndpointsCard ) ExitSelectMode () {
268277 r .selectMode = false
278+ r .scrollOffset = 0
269279}
270280
271281func (r * EndpointsCard ) IsInSelectMode () bool {
@@ -300,6 +310,7 @@ func (r *EndpointsCard) HasSelection() bool {
300310func (r * EndpointsCard ) ClearSelection () {
301311 r .selectedIndex = 0
302312 r .selectMode = false
313+ r .scrollOffset = 0
303314}
304315
305316// GetSelectedEndpoint returns the currently selected endpoint filter, or nil if none selected
@@ -314,3 +325,22 @@ func (r *EndpointsCard) GetSelectedEndpoint() *EndpointFilter {
314325 Status : ep .status ,
315326 }
316327}
328+
329+ func (r * EndpointsCard ) ensureSelectionVisible (height int ) {
330+ if height <= 0 || len (r .sorted ) == 0 {
331+ r .scrollOffset = 0
332+ return
333+ }
334+
335+ maxOffset := max (len (r .sorted )- height , 0 )
336+ r .scrollOffset = min (max (r .scrollOffset , 0 ), maxOffset )
337+
338+ if r .selectedIndex < r .scrollOffset {
339+ r .scrollOffset = r .selectedIndex
340+ }
341+ if r .selectedIndex >= r .scrollOffset + height {
342+ r .scrollOffset = r .selectedIndex - height + 1
343+ }
344+
345+ r .scrollOffset = min (max (r .scrollOffset , 0 ), maxOffset )
346+ }
0 commit comments