Commit 92cd9b0

Eric Bower  ·  2026-01-16 15:47:14 -0500 EST
parent 62cd4d9
feat(tui.logs): allow logs to be manually scrolled
2 files changed,  +63, -8
+4, -0
......@@ -39,6 +39,10 @@ func (m *TextInput) FocusOut() (vxfw.Command, error) {
3939 return vxfw.RedrawCmd{}, nil
4040 }
4141
42+func (m *TextInput) Focused() bool {
43+ return m.focus
44+}
45+
4246 func (m *TextInput) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Command, error) {
4347 return nil, nil
4448 }
+59, -8
......@@ -31,6 +31,7 @@ type LogsPage struct {
3131 logs []*LogLine
3232 ctx context.Context
3333 done context.CancelFunc
34+ focus string
3435 }
3536
3637 func NewLogsPage(shrd *SharedModel) *LogsPage {
......@@ -38,12 +39,16 @@ func NewLogsPage(shrd *SharedModel) *LogsPage {
3839 shared: shrd,
3940 input: NewTextInput("filter logs"),
4041 }
41- page.list = &list.Dynamic{Builder: page.getWidget, DisableEventHandlers: true}
42+ page.list = &list.Dynamic{Builder: page.getWidget, DrawCursor: true}
4243 return page
4344 }
4445
4546 func (m *LogsPage) Footer() []Shortcut {
46- return []Shortcut{}
47+ return []Shortcut{
48+ {Shortcut: "tab", Text: "toggle focus"},
49+ {Shortcut: "↑↓", Text: "scroll"},
50+ {Shortcut: "G", Text: "bottom"},
51+ }
4752 }
4853
4954 func (m *LogsPage) filterLogLine(match string, ll *LogLine) bool {
......@@ -73,19 +78,28 @@ func (m *LogsPage) filterLogs() {
7378 filtered = append(filtered, idx)
7479 }
7580 }
81+
82+ // Check if cursor is at the last position before filtering
83+ isAtBottom := len(m.filtered) == 0 || int(m.list.Cursor()) == len(m.filtered)-1
84+
7685 m.filtered = filtered
7786
78- // scroll to bottom
79- if len(m.filtered) > 0 {
87+ // scroll to bottom only if we were already at the bottom
88+ if isAtBottom && len(m.filtered) > 0 {
8089 m.list.SetCursor(uint(len(m.filtered) - 1))
8190 }
8291 }
8392
8493 func (m *LogsPage) CaptureEvent(ev vaxis.Event) (vxfw.Command, error) {
85- switch ev.(type) {
94+ switch msg := ev.(type) {
8695 case vaxis.Key:
87- m.filterLogs()
88- return vxfw.RedrawCmd{}, nil
96+ if msg.Matches(vaxis.KeyTab) {
97+ return nil, nil
98+ }
99+ if m.focus == "input" {
100+ m.filterLogs()
101+ return vxfw.RedrawCmd{}, nil
102+ }
89103 }
90104 return nil, nil
91105 }
......@@ -96,9 +110,35 @@ func (m *LogsPage) HandleEvent(ev vaxis.Event, phase vxfw.EventPhase) (vxfw.Comm
96110 go func() {
97111 _ = m.connectToLogs()
98112 }()
113+ m.focus = "input"
99114 return m.input.FocusIn()
100115 case PageOut:
101116 m.done()
117+ case vaxis.Key:
118+ if msg.Matches(vaxis.KeyTab) {
119+ if m.focus == "input" {
120+ m.focus = "list"
121+ m.filterLogs()
122+ cmd, _ := m.input.FocusOut()
123+ return vxfw.BatchCmd([]vxfw.Command{
124+ vxfw.FocusWidgetCmd(m.list),
125+ cmd,
126+ }), nil
127+ }
128+ m.focus = "input"
129+ cmd, _ := m.input.FocusIn()
130+ return vxfw.BatchCmd([]vxfw.Command{
131+ cmd,
132+ vxfw.RedrawCmd{},
133+ }), nil
134+ }
135+ if msg.Matches('G') {
136+ // Scroll to bottom
137+ if len(m.filtered) > 0 {
138+ m.list.SetCursor(uint(len(m.filtered) - 1))
139+ return vxfw.RedrawCmd{}, nil
140+ }
141+ }
102142 case LogLineLoaded:
103143 ll := NewLogLine(msg.Line)
104144 m.logs = append(m.logs, ll)
......@@ -116,7 +156,10 @@ func (m *LogsPage) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) {
116156 txtSurf, _ := txt.Draw(ctx)
117157 root.AddChild(0, 0, txtSurf)
118158 } else {
119- listSurf, _ := m.list.Draw(createDrawCtx(ctx, ctx.Max.Height-4))
159+ listPane := NewBorder(m.list)
160+ listPane.Label = "logs"
161+ m.focusBorder(listPane)
162+ listSurf, _ := listPane.Draw(createDrawCtx(ctx, ctx.Max.Height-4))
120163 root.AddChild(0, 0, listSurf)
121164 }
122165
......@@ -126,6 +169,14 @@ func (m *LogsPage) Draw(ctx vxfw.DrawContext) (vxfw.Surface, error) {
126169 return root, nil
127170 }
128171
172+func (m *LogsPage) focusBorder(border *Border) {
173+ if m.focus == "list" {
174+ border.Style = vaxis.Style{Foreground: oj}
175+ } else {
176+ border.Style = vaxis.Style{Foreground: purp}
177+ }
178+}
179+
129180 func (m *LogsPage) getWidget(i uint, cursor uint) vxfw.Widget {
130181 if len(m.filtered) == 0 {
131182 return nil