1package chat
2
3import (
4 "strings"
5
6 "charm.land/lipgloss/v2"
7 "github.com/charmbracelet/crush/internal/message"
8 "github.com/charmbracelet/crush/internal/ui/attachments"
9 "github.com/charmbracelet/crush/internal/ui/common"
10 "github.com/charmbracelet/crush/internal/ui/styles"
11)
12
13// UserMessageItem represents a user message in the chat UI.
14type UserMessageItem struct {
15 *highlightableMessageItem
16 *cachedMessageItem
17 *focusableMessageItem
18
19 attachments *attachments.Renderer
20 message *message.Message
21 sty *styles.Styles
22}
23
24// NewUserMessageItem creates a new UserMessageItem.
25func NewUserMessageItem(sty *styles.Styles, message *message.Message, attachments *attachments.Renderer) MessageItem {
26 return &UserMessageItem{
27 highlightableMessageItem: defaultHighlighter(sty),
28 cachedMessageItem: &cachedMessageItem{},
29 focusableMessageItem: &focusableMessageItem{},
30 attachments: attachments,
31 message: message,
32 sty: sty,
33 }
34}
35
36// RawRender implements [MessageItem].
37func (m *UserMessageItem) RawRender(width int) string {
38 cappedWidth := cappedMessageWidth(width)
39
40 content, height, ok := m.getCachedRender(cappedWidth)
41 // cache hit
42 if ok {
43 return m.renderHighlighted(content, cappedWidth, height)
44 }
45
46 renderer := common.MarkdownRenderer(m.sty, cappedWidth)
47
48 msgContent := strings.TrimSpace(m.message.Content().Text)
49 result, err := renderer.Render(msgContent)
50 if err != nil {
51 content = msgContent
52 } else {
53 content = strings.TrimSuffix(result, "\n")
54 }
55
56 if len(m.message.BinaryContent()) > 0 {
57 attachmentsStr := m.renderAttachments(cappedWidth)
58 if content == "" {
59 content = attachmentsStr
60 } else {
61 content = strings.Join([]string{content, "", attachmentsStr}, "\n")
62 }
63 }
64
65 height = lipgloss.Height(content)
66 m.setCachedRender(content, cappedWidth, height)
67 return m.renderHighlighted(content, cappedWidth, height)
68}
69
70// Render implements MessageItem.
71func (m *UserMessageItem) Render(width int) string {
72 style := m.sty.Chat.Message.UserBlurred
73 if m.focused {
74 style = m.sty.Chat.Message.UserFocused
75 }
76 return style.Render(m.RawRender(width))
77}
78
79// ID implements MessageItem.
80func (m *UserMessageItem) ID() string {
81 return m.message.ID
82}
83
84// renderAttachments renders attachments.
85func (m *UserMessageItem) renderAttachments(width int) string {
86 var attachments []message.Attachment
87 for _, at := range m.message.BinaryContent() {
88 attachments = append(attachments, message.Attachment{
89 FileName: at.Path,
90 MimeType: at.MIMEType,
91 })
92 }
93 return m.attachments.Render(attachments, false, width)
94}