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// Render implements MessageItem.
37func (m *UserMessageItem) Render(width int) string {
38 cappedWidth := cappedMessageWidth(width)
39
40 style := m.sty.Chat.Message.UserBlurred
41 if m.focused {
42 style = m.sty.Chat.Message.UserFocused
43 }
44
45 content, height, ok := m.getCachedRender(cappedWidth)
46 // cache hit
47 if ok {
48 return style.Render(m.renderHighlighted(content, cappedWidth, height))
49 }
50
51 renderer := common.MarkdownRenderer(m.sty, cappedWidth)
52
53 msgContent := strings.TrimSpace(m.message.Content().Text)
54 result, err := renderer.Render(msgContent)
55 if err != nil {
56 content = msgContent
57 } else {
58 content = strings.TrimSuffix(result, "\n")
59 }
60
61 if len(m.message.BinaryContent()) > 0 {
62 attachmentsStr := m.renderAttachments(cappedWidth)
63 if content == "" {
64 content = attachmentsStr
65 } else {
66 content = strings.Join([]string{content, "", attachmentsStr}, "\n")
67 }
68 }
69
70 height = lipgloss.Height(content)
71 m.setCachedRender(content, cappedWidth, height)
72 return style.Render(m.renderHighlighted(content, cappedWidth, height))
73}
74
75// ID implements MessageItem.
76func (m *UserMessageItem) ID() string {
77 return m.message.ID
78}
79
80// renderAttachments renders attachments.
81func (m *UserMessageItem) renderAttachments(width int) string {
82 var attachments []message.Attachment
83 for _, at := range m.message.BinaryContent() {
84 attachments = append(attachments, message.Attachment{
85 FileName: at.Path,
86 MimeType: at.MIMEType,
87 })
88 }
89 return m.attachments.Render(attachments, false, width)
90}