@@ -10,17 +10,33 @@ import (
1010)
1111
1212type Help struct {
13- bindings [][]key.Binding
14- theme theme.Theme
13+ bindings [][]key.Binding
14+ theme theme.Theme
15+ scrollOffset int
1516}
1617
1718func NewHelp (bindings [][]key.Binding , t theme.Theme ) Help {
1819 return Help {bindings : bindings , theme : t }
1920}
2021
22+ func (h Help ) WithTheme (t theme.Theme ) Help {
23+ h .theme = t
24+ return h
25+ }
26+
2127func (h Help ) Init () tea.Cmd { return nil }
2228
23- func (h Help ) Update (msg tea.Msg ) (Screen , tea.Cmd ) { return h , nil }
29+ func (h Help ) Update (msg tea.Msg ) (Screen , tea.Cmd ) {
30+ if msg , ok := msg .(tea.KeyPressMsg ); ok {
31+ switch msg .String () {
32+ case "up" , "k" :
33+ h .scrollOffset = max (0 , h .scrollOffset - 1 )
34+ case "down" , "j" :
35+ h .scrollOffset ++
36+ }
37+ }
38+ return h , nil
39+ }
2440
2541func (h Help ) View (width , height int ) string {
2642 rule := func (label string ) string {
@@ -29,42 +45,74 @@ func (h Help) View(width, height int) string {
2945 return h .theme .Title .Render (label ) + h .theme .PaletteAccent .Render (" " + fill )
3046 }
3147 binding := func (k , desc string ) string {
32- return " " + h .theme .Accent .Render (k ) + " " + h .theme .Muted .Render (desc ) + " \n "
48+ return " " + h .theme .Accent .Render (k ) + " " + h .theme .Muted .Render (desc )
3349 }
3450
35- var b strings.Builder
36- b .WriteString (rule ("Global keys" ) + "\n \n " )
51+ var lines []string
52+ add := func (s string ) { lines = append (lines , s ) }
53+
54+ add (rule ("Global keys" ))
55+ add ("" )
3756 for _ , group := range h .bindings {
3857 for _ , kb := range group {
3958 help := kb .Help ()
40- b . WriteString (binding (help .Key , help .Desc ))
59+ add (binding (help .Key , help .Desc ))
4160 }
4261 }
4362
44- b .WriteString ("\n " + rule ("Dashboard" ) + "\n \n " )
45- b .WriteString (binding ("j/k" , "navigate files" ))
46- b .WriteString (binding ("enter" , "open selected file/folder in terminal" ))
47- b .WriteString (binding ("r" , "refresh + trigger rescan" ))
48-
49- b .WriteString ("\n " + rule ("Connect" ) + "\n \n " )
50- b .WriteString (binding ("j/k" , "navigate devices" ))
51- b .WriteString (binding ("p" , "pair a new device by pasting its ID" ))
52- b .WriteString (binding ("a" , "accept a pending device" ))
53- b .WriteString (binding ("i" , "ignore a pending device" ))
54- b .WriteString (binding ("d" , "remove an approved device" ))
55- b .WriteString (binding ("r" , "rename a device" ))
56-
57- b .WriteString ("\n " + rule ("Settings" ) + "\n \n " )
58- b .WriteString (binding ("c" , "copy this device's ID to clipboard" ))
59-
60- b .WriteString ("\n " + rule ("Command Palette" ) + "\n \n " )
61- b .WriteString (binding ("ctrl+k" , "open palette" ))
62- b .WriteString (binding ("ctrl+t" , "cycle to next theme" ))
63- b .WriteString (binding ("type" , "filter commands" ))
64- b .WriteString (binding ("enter" , "run selected command" ))
65- b .WriteString (binding ("esc" , "close palette" ))
66-
67- return lipgloss .NewStyle ().Width (width ).Height (height ).Render (b .String ())
63+ add ("" )
64+ add (rule ("Dashboard" ))
65+ add ("" )
66+ add (binding ("j/k" , "navigate files" ))
67+ add (binding ("enter" , "open selected file/folder in terminal" ))
68+ add (binding ("r" , "refresh + trigger rescan" ))
69+
70+ add ("" )
71+ add (rule ("Connect" ))
72+ add ("" )
73+ add (binding ("j/k" , "navigate devices" ))
74+ add (binding ("p" , "pair a new device by pasting its ID" ))
75+ add (binding ("a" , "accept a pending device" ))
76+ add (binding ("i" , "ignore a pending device" ))
77+ add (binding ("d" , "remove an approved device" ))
78+ add (binding ("r" , "rename a device" ))
79+
80+ add ("" )
81+ add (rule ("Settings" ))
82+ add ("" )
83+ add (binding ("c" , "copy this device's ID to clipboard" ))
84+
85+ add ("" )
86+ add (rule ("Command Palette" ))
87+ add ("" )
88+ add (binding ("ctrl+k" , "open palette" ))
89+ add (binding ("ctrl+t" , "cycle to next theme" ))
90+ add (binding ("type" , "filter commands" ))
91+ add (binding ("enter" , "run selected command" ))
92+ add (binding ("esc" , "close palette" ))
93+
94+ total := len (lines )
95+ if total <= height {
96+ // no scrolling needed
97+ return lipgloss .NewStyle ().Width (width ).Height (height ).Render (strings .Join (lines , "\n " ))
98+ }
99+
100+ // clamp scroll so we never go past the last page
101+ maxOffset := total - height + 1 // +1 reserves space for the indicator
102+ h .scrollOffset = min (h .scrollOffset , maxOffset )
103+
104+ visible := lines [h .scrollOffset : h .scrollOffset + height - 1 ]
105+
106+ // indicator line
107+ var indicator string
108+ if h .scrollOffset < maxOffset {
109+ indicator = h .theme .Muted .Render ("↓ j/k to scroll" )
110+ } else {
111+ indicator = h .theme .Muted .Render ("↑ j/k to scroll" )
112+ }
113+
114+ content := strings .Join (append (visible , indicator ), "\n " )
115+ return lipgloss .NewStyle ().Width (width ).Height (height ).Render (content )
68116}
69117
70118func (h Help ) Title () string { return "Help" }
0 commit comments