@@ -13,10 +13,13 @@ type Help struct {
1313 bindings [][]key.Binding
1414 theme theme.Theme
1515 scrollOffset int
16+ totalLines int
1617}
1718
1819func NewHelp (bindings [][]key.Binding , t theme.Theme ) Help {
19- return Help {bindings : bindings , theme : t }
20+ h := Help {bindings : bindings , theme : t }
21+ h .totalLines = len (h .buildLines ())
22+ return h
2023}
2124
2225func (h Help ) WithTheme (t theme.Theme ) Help {
@@ -32,19 +35,44 @@ func (h Help) Update(msg tea.Msg) (Screen, tea.Cmd) {
3235 case "up" , "k" :
3336 h .scrollOffset = max (0 , h .scrollOffset - 1 )
3437 case "down" , "j" :
35- h .scrollOffset ++
38+ h .scrollOffset = min ( h . scrollOffset + 1 , max ( 0 , h . totalLines - 1 ))
3639 }
3740 }
3841 return h , nil
3942}
4043
4144func (h Help ) View (width , height int ) string {
45+ lines := h .buildLines ()
46+ total := len (lines )
47+
48+ if total <= height {
49+ return lipgloss .NewStyle ().Width (width ).Height (height ).Render (strings .Join (lines , "\n " ))
50+ }
51+
52+ // tighter clamp now that we know height
53+ maxOffset := max (0 , total - height + 1 )
54+ offset := min (h .scrollOffset , maxOffset )
55+
56+ visible := lines [offset : offset + height - 1 ]
57+
58+ var indicator string
59+ if offset < maxOffset {
60+ indicator = h .theme .Muted .Render ("↓ j/k to scroll" )
61+ } else {
62+ indicator = h .theme .Muted .Render ("↑ j/k to scroll" )
63+ }
64+
65+ return lipgloss .NewStyle ().Width (width ).Height (height ).Render (
66+ strings .Join (append (visible , indicator ), "\n " ),
67+ )
68+ }
69+
70+ // buildLines returns the full list of rendered lines for the Help screen.
71+ func (h Help ) buildLines () []string {
4272 rule := func (label string ) string {
43- labelW := lipgloss .Width (label )
44- fill := strings .Repeat ("/" , max (0 , width - labelW - 1 ))
45- return h .theme .Title .Render (label ) + h .theme .PaletteAccent .Render (" " + fill )
73+ return h .theme .Title .Render (label ) + h .theme .PaletteAccent .Render (" ////" )
4674 }
47- binding := func (k , desc string ) string {
75+ bind := func (k , desc string ) string {
4876 return " " + h .theme .Accent .Render (k ) + " " + h .theme .Muted .Render (desc )
4977 }
5078
@@ -56,63 +84,42 @@ func (h Help) View(width, height int) string {
5684 for _ , group := range h .bindings {
5785 for _ , kb := range group {
5886 help := kb .Help ()
59- add (binding (help .Key , help .Desc ))
87+ add (bind (help .Key , help .Desc ))
6088 }
6189 }
6290
6391 add ("" )
6492 add (rule ("Dashboard" ))
6593 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" ))
94+ add (bind ("j/k" , "navigate files" ))
95+ add (bind ("enter" , "open selected file/folder in terminal" ))
96+ add (bind ("r" , "refresh + trigger rescan" ))
6997
7098 add ("" )
7199 add (rule ("Connect" ))
72100 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" ))
101+ add (bind ("j/k" , "navigate devices" ))
102+ add (bind ("p" , "pair a new device by pasting its ID" ))
103+ add (bind ("a" , "accept a pending device" ))
104+ add (bind ("i" , "ignore a pending device" ))
105+ add (bind ("d" , "remove an approved device" ))
106+ add (bind ("r" , "rename a device" ))
79107
80108 add ("" )
81109 add (rule ("Settings" ))
82110 add ("" )
83- add (binding ("c" , "copy this device's ID to clipboard" ))
111+ add (bind ("c" , "copy this device's ID to clipboard" ))
84112
85113 add ("" )
86114 add (rule ("Command Palette" ))
87115 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- }
116+ add (bind ("ctrl+k" , "open palette" ))
117+ add (bind ("ctrl+t" , "cycle to next theme" ))
118+ add (bind ("type" , "filter commands" ))
119+ add (bind ("enter" , "run selected command" ))
120+ add (bind ("esc" , "close palette" ))
113121
114- content := strings .Join (append (visible , indicator ), "\n " )
115- return lipgloss .NewStyle ().Width (width ).Height (height ).Render (content )
122+ return lines
116123}
117124
118125func (h Help ) Title () string { return "Help" }
0 commit comments