@@ -14,6 +14,11 @@ import (
1414 "github.com/docker/docker-agent/pkg/tui/types"
1515)
1616
17+ const (
18+ maxUserMessageLines = 30
19+ collapsedUserMessageLines = 5
20+ )
21+
1722// Model represents a view that can render a message
1823type Model interface {
1924 layout.Model
@@ -34,6 +39,7 @@ type messageModel struct {
3439 focused bool
3540 selected bool
3641 hovered bool
42+ expanded bool
3743 spinner spinner.Spinner
3844
3945 // renderCache memoizes the output of Render(width) keyed by the inputs
@@ -65,6 +71,7 @@ type renderCache struct {
6571 width int
6672 selected bool
6773 hovered bool
74+ expanded bool
6875 sameAgent bool
6976 result string
7077}
@@ -128,6 +135,33 @@ func (mv *messageModel) Update(msg tea.Msg) (layout.Model, tea.Cmd) {
128135 return mv , nil
129136}
130137
138+ // Toggle switches between expanded and collapsed state.
139+ func (mv * messageModel ) Toggle () {
140+ mv .expanded = ! mv .expanded
141+ mv .renderCache .valid = false
142+ }
143+
144+ // IsToggleLine returns true if the line contains the expand/collapse affordance.
145+ func (mv * messageModel ) IsToggleLine (lineIdx int ) bool {
146+ if mv .message == nil || mv .message .Type != types .MessageTypeUser {
147+ return false
148+ }
149+ content := strings .TrimRight (mv .message .Content , "\n \r \t " )
150+ if strings .Count (content , "\n " )+ 1 <= maxUserMessageLines {
151+ return false
152+ }
153+
154+ // The indicator is placed at the end of the message view with a leading \n\n.
155+ // Depending on edit state, the view has 0 or 1 lines of top padding,
156+ // and 1 line of bottom padding.
157+ // height-1 is the bottom padding.
158+ // height-2 is the text of the indicator ("[-] click to collapse").
159+ // height-3 is the empty line above it.
160+ // By checking >= height-3, we provide a generous clickable area exactly on the toggle.
161+ height := mv .Height (mv .width )
162+ return lineIdx >= height - 3
163+ }
164+
131165// View renders the message view
132166func (mv * messageModel ) View () string {
133167 return mv .Render (mv .width )
@@ -151,6 +185,7 @@ func (mv *messageModel) Render(width int) string {
151185 c .msgType == msg .Type &&
152186 c .selected == mv .selected &&
153187 c .hovered == mv .hovered &&
188+ c .expanded == mv .expanded &&
154189 c .content == msg .Content &&
155190 c .sameAgent == mv .sameAgentAsPrevious (msg ) {
156191 return c .result
@@ -167,6 +202,7 @@ func (mv *messageModel) Render(width int) string {
167202 width : width ,
168203 selected : mv .selected ,
169204 hovered : mv .hovered ,
205+ expanded : mv .expanded ,
170206 sameAgent : mv .sameAgentAsPrevious (msg ),
171207 result : result ,
172208 }
@@ -199,16 +235,34 @@ func (mv *messageModel) render(width int) string {
199235 messageStyle = styles .SelectedUserMessageStyle
200236 }
201237
238+ formatUserContent := func (c string ) string {
239+ c = strings .TrimRight (c , "\n \r \t " )
240+ if c == "" {
241+ return msg .Content
242+ }
243+
244+ totalLines := strings .Count (c , "\n " ) + 1
245+ if totalLines > maxUserMessageLines {
246+ if ! mv .expanded {
247+ parts := strings .SplitN (c , "\n " , collapsedUserMessageLines + 1 )
248+ visibleLines := strings .Join (parts [:collapsedUserMessageLines ], "\n " )
249+ hiddenCount := totalLines - collapsedUserMessageLines
250+ indicator := "\n \n " + styles .MutedStyle .Render (fmt .Sprintf ("[+] expand %d more lines" , hiddenCount ))
251+ return visibleLines + indicator
252+ }
253+ indicator := "\n \n " + styles .MutedStyle .Render ("[-] collapse" )
254+ return c + indicator
255+ }
256+ return c
257+ }
258+
202259 if msg .SessionPosition == nil {
203- return messageStyle .Width (width ).Render (msg .Content )
260+ return messageStyle .Width (width ).Render (formatUserContent ( msg .Content ) )
204261 }
205262
206263 // For editable messages, place the pencil icon in the top padding row
207264 innerWidth := width - messageStyle .GetHorizontalFrameSize ()
208- content := strings .TrimRight (msg .Content , "\n \r \t " )
209- if content == "" {
210- content = msg .Content
211- }
265+ content := formatUserContent (msg .Content )
212266
213267 // Create the edit icon for the top row
214268 editIcon := styles .MutedStyle .Render (types .UserMessageEditLabel )
0 commit comments