11package components
22
33import (
4+ "fmt"
45 "strings"
56
67 tea "charm.land/bubbletea/v2"
@@ -29,6 +30,9 @@ type AutocompleteSelectedMsg struct {
2930 Command string
3031}
3132
33+ // maxVisibleItems is the maximum number of autocomplete items shown at once.
34+ const maxVisibleItems = 10
35+
3236// NewAutocomplete creates an autocomplete model with all known commands.
3337func NewAutocomplete () AutocompleteModel {
3438 commands := []CommandEntry {
@@ -40,6 +44,18 @@ func NewAutocomplete() AutocompleteModel {
4044 {Name : "/sessions" , Desc : "List sessions" },
4145 {Name : "/provider" , Desc : "Provider management" },
4246 {Name : "/exit" , Desc : "Quit ratchet" },
47+ {Name : "/plan" , Desc : "Show plan mode info" },
48+ {Name : "/approve" , Desc : "Approve a proposed plan" },
49+ {Name : "/reject" , Desc : "Reject a proposed plan" },
50+ {Name : "/fleet" , Desc : "Start fleet execution for a plan" },
51+ {Name : "/team" , Desc : "Team management" },
52+ {Name : "/review" , Desc : "Run code-reviewer on current git diff" },
53+ {Name : "/compact" , Desc : "Compress conversation context" },
54+ {Name : "/loop" , Desc : "Schedule a recurring command" },
55+ {Name : "/cron" , Desc : "Schedule with cron expression" },
56+ {Name : "/mcp" , Desc : "MCP tool management" },
57+ {Name : "/jobs" , Desc : "Show job control panel" },
58+ {Name : "/login" , Desc : "Re-authenticate provider" },
4359 }
4460 return AutocompleteModel {commands : commands }
4561}
@@ -109,7 +125,7 @@ func (m AutocompleteModel) Update(msg tea.Msg) (AutocompleteModel, tea.Cmd) {
109125 return m , nil
110126}
111127
112- // View renders the autocomplete dropdown.
128+ // View renders the autocomplete dropdown, capped at maxVisibleItems rows .
113129func (m AutocompleteModel ) View (t theme.Theme , width int ) string {
114130 if ! m .visible || len (m .matches ) == 0 {
115131 return ""
@@ -130,17 +146,50 @@ func (m AutocompleteModel) View(t theme.Theme, width int) string {
130146 Foreground (lipgloss .Color ("#FFFFFF" )).
131147 Width (maxWidth )
132148
149+ mutedStyle := lipgloss .NewStyle ().
150+ Foreground (t .Muted ).
151+ Width (maxWidth )
152+
153+ total := len (m .matches )
154+
155+ // Compute a window of maxVisibleItems around the cursor.
156+ start := m .cursor - maxVisibleItems / 2
157+ if start < 0 {
158+ start = 0
159+ }
160+ end := start + maxVisibleItems
161+ if end > total {
162+ end = total
163+ start = end - maxVisibleItems
164+ if start < 0 {
165+ start = 0
166+ }
167+ }
168+
133169 var sb strings.Builder
134- for i , cmd := range m .matches {
135- line := " " + cmd .Name + " " + cmd .Desc
170+ first := true
171+
172+ if start > 0 {
173+ sb .WriteString (mutedStyle .Render (fmt .Sprintf (" ↑ %d more" , start )))
174+ first = false
175+ }
176+
177+ for i := start ; i < end ; i ++ {
178+ if ! first {
179+ sb .WriteString ("\n " )
180+ }
181+ first = false
182+ line := " " + m .matches [i ].Name + " " + m .matches [i ].Desc
136183 if i == m .cursor {
137184 sb .WriteString (selectedStyle .Render (line ))
138185 } else {
139186 sb .WriteString (style .Render (line ))
140187 }
141- if i < len (m .matches )- 1 {
142- sb .WriteString ("\n " )
143- }
188+ }
189+
190+ if end < total {
191+ sb .WriteString ("\n " )
192+ sb .WriteString (mutedStyle .Render (fmt .Sprintf (" ↓ %d more" , total - end )))
144193 }
145194
146195 return lipgloss .NewStyle ().
0 commit comments