Initilaize
This commit is contained in:
201
internal/ui/chatarea/model.go
Normal file
201
internal/ui/chatarea/model.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package chatarea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"lazykimi/pkg/theme"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/v2/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea/v2"
|
||||
"github.com/charmbracelet/glamour"
|
||||
"github.com/charmbracelet/lipgloss/v2"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
|
||||
"github.com/muesli/reflow/wordwrap"
|
||||
)
|
||||
|
||||
type Style struct {
|
||||
UserMessage lipgloss.Style
|
||||
AssistantMessage lipgloss.Style
|
||||
SystemMessage lipgloss.Style
|
||||
Divider lipgloss.Style
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
Width, Height int
|
||||
|
||||
systemPrompt string
|
||||
Messages []openai.ChatCompletionMessage
|
||||
streamingResponse string
|
||||
|
||||
markdownMode bool
|
||||
vp viewport.Model
|
||||
glmr *glamour.TermRenderer
|
||||
style Style
|
||||
|
||||
ready bool
|
||||
}
|
||||
|
||||
func (m *Model) SetMarkdownMode(markdown bool) {
|
||||
m.markdownMode = markdown
|
||||
}
|
||||
|
||||
func New(width, height int) Model {
|
||||
vp := viewport.New()
|
||||
vp.Style = lipgloss.NewStyle().
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderForeground(theme.Gray).
|
||||
Padding(0, 0)
|
||||
vp.MouseWheelEnabled = true
|
||||
|
||||
baseMessage := lipgloss.NewStyle().Padding(0, 2).Bold(true)
|
||||
|
||||
return Model{
|
||||
vp: vp,
|
||||
style: Style{
|
||||
UserMessage: baseMessage.Foreground(theme.Yellow),
|
||||
AssistantMessage: baseMessage.Foreground(theme.Blue),
|
||||
SystemMessage: baseMessage.Foreground(theme.Green),
|
||||
Divider: lipgloss.NewStyle().Foreground(theme.Gray),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||
var cmd tea.Cmd
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
m.Width = msg.Width
|
||||
m.Height = msg.Height - 3
|
||||
m.vp.SetWidth(m.Width)
|
||||
m.vp.SetHeight(m.Height)
|
||||
|
||||
// // Initialize markdown renderer
|
||||
// gr, err := glamour.NewTermRenderer(
|
||||
// glamour.WithAutoStyle(),
|
||||
// glamour.WithEmoji(),
|
||||
// glamour.WithWordWrap(m.Width-6),
|
||||
// )
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// m.glmr = gr
|
||||
m.ready = true
|
||||
}
|
||||
m.vp, cmd = m.vp.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m Model) View() string {
|
||||
if !m.ready {
|
||||
return ""
|
||||
}
|
||||
var messages []string
|
||||
divider := m.style.Divider.Render(strings.Repeat("─", m.Width-6))
|
||||
|
||||
// Render history messages
|
||||
for _, msg := range m.Messages {
|
||||
messages = append(messages, m.formatMessage(msg))
|
||||
messages = append(messages, divider)
|
||||
}
|
||||
|
||||
// Render current response if loading
|
||||
if m.streamingResponse != "" {
|
||||
messages = append(messages, m.formatMessage(openai.ChatCompletionMessage{
|
||||
Role: openai.ChatMessageRoleAssistant,
|
||||
Content: m.streamingResponse,
|
||||
}))
|
||||
}
|
||||
|
||||
// Set content with proper width
|
||||
m.vp.SetContent(strings.Join(messages, "\n"))
|
||||
|
||||
// // Auto-scroll to bottom if we weren't already at the bottom
|
||||
// // before this update
|
||||
if !m.vp.AtBottom() {
|
||||
m.vp.GotoBottom()
|
||||
}
|
||||
|
||||
return m.vp.View()
|
||||
}
|
||||
|
||||
// AddUserMessage adds a user message to the chat
|
||||
func (c *Model) AddUserMessage(content string) {
|
||||
c.Messages = append(c.Messages, openai.ChatCompletionMessage{
|
||||
Role: openai.ChatMessageRoleUser,
|
||||
Content: content,
|
||||
})
|
||||
}
|
||||
|
||||
// AddAssistantMessage adds an assistant message to the chat
|
||||
func (c *Model) AddAssistantMessage(content string) {
|
||||
c.Messages = append(c.Messages, openai.ChatCompletionMessage{
|
||||
Role: openai.ChatMessageRoleAssistant,
|
||||
Content: content,
|
||||
})
|
||||
}
|
||||
|
||||
// AppendCurrentResponse updates the current streaming response
|
||||
func (c *Model) AppendCurrentResponse(content string) {
|
||||
c.streamingResponse += content
|
||||
}
|
||||
|
||||
// CommitCurrentResponse adds the current response as a message and clears it
|
||||
func (c *Model) CommitCurrentResponse() {
|
||||
if c.streamingResponse != "" {
|
||||
c.AddAssistantMessage(c.streamingResponse)
|
||||
c.streamingResponse = ""
|
||||
}
|
||||
}
|
||||
|
||||
// Clear clears all messages except the system prompt
|
||||
func (c *Model) Clear() {
|
||||
c.Messages = []openai.ChatCompletionMessage{}
|
||||
c.streamingResponse = ""
|
||||
|
||||
// Re-add system message if set
|
||||
if c.systemPrompt != "" {
|
||||
c.Messages = append(c.Messages, openai.ChatCompletionMessage{
|
||||
Role: openai.ChatMessageRoleSystem,
|
||||
Content: c.systemPrompt,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// SetSystemPrompt sets a new system prompt and updates the first message if needed
|
||||
func (c *Model) SetSystemPrompt(prompt string) {
|
||||
c.systemPrompt = prompt
|
||||
c.Clear() // This will add the new system prompt
|
||||
}
|
||||
|
||||
func (m *Model) formatMessage(msg openai.ChatCompletionMessage) string {
|
||||
var (
|
||||
indicator string
|
||||
style lipgloss.Style
|
||||
useMd bool
|
||||
)
|
||||
|
||||
switch msg.Role {
|
||||
case openai.ChatMessageRoleUser:
|
||||
indicator = "You"
|
||||
style = m.style.UserMessage
|
||||
case openai.ChatMessageRoleAssistant:
|
||||
indicator = "AI"
|
||||
style = m.style.AssistantMessage
|
||||
useMd = m.markdownMode
|
||||
case openai.ChatMessageRoleSystem:
|
||||
indicator = "System"
|
||||
style = m.style.SystemMessage
|
||||
useMd = m.markdownMode
|
||||
}
|
||||
|
||||
content := strings.TrimSpace(msg.Content)
|
||||
content = wordwrap.String(content, m.Width-3)
|
||||
|
||||
if useMd {
|
||||
renderedContent, _ := m.glmr.Render(content)
|
||||
return fmt.Sprintf("%s\n%s", indicator, renderedContent)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s\n%s", indicator, style.Render(content))
|
||||
}
|
||||
Reference in New Issue
Block a user