user.go

 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}