Skip to content

Commit 8bf4b40

Browse files
committed
added markdown rendering for model output
1 parent 7ce33af commit 8bf4b40

File tree

1 file changed

+126
-54
lines changed

1 file changed

+126
-54
lines changed

internal/chat/interface.go

Lines changed: 126 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/charmbracelet/bubbles/textinput"
99
"github.com/charmbracelet/bubbles/viewport"
1010
tea "github.com/charmbracelet/bubbletea"
11+
"github.com/charmbracelet/glamour"
1112
"github.com/charmbracelet/lipgloss"
1213
"github.com/marpit19/charmlama/internal/ollama"
1314
)
@@ -21,10 +22,18 @@ var (
2122
Foreground(lipgloss.Color("#00FFFF")). // Cyan
2223
Bold(true)
2324

24-
inputStyle = lipgloss.NewStyle().
25-
BorderStyle(lipgloss.NormalBorder()).
26-
BorderForeground(lipgloss.Color("#FF1493")). // Deep Pink
27-
Foreground(lipgloss.Color("#FFFFFF")) // White text
25+
activeInputStyle = lipgloss.NewStyle().
26+
BorderStyle(lipgloss.NormalBorder()).
27+
BorderForeground(lipgloss.Color("#FF1493")). // Deep Pink
28+
Foreground(lipgloss.Color("#FFFFFF")) // White text
29+
30+
disabledInputStyle = lipgloss.NewStyle().
31+
BorderStyle(lipgloss.NormalBorder()).
32+
BorderForeground(lipgloss.Color("#696969")). // Dim Gray
33+
Foreground(lipgloss.Color("#A9A9A9")) // Dark Gray text
34+
35+
statusStyle = lipgloss.NewStyle().
36+
Foreground(lipgloss.Color("#98FB98"))
2837
)
2938

3039
type ChatInterface struct {
@@ -39,6 +48,7 @@ type ChatInterface struct {
3948
spinner spinner.Model
4049
width int
4150
height int
51+
renderer *glamour.TermRenderer
4252
}
4353

4454
func NewChatInterface(model string, manager *ollama.Manager) *ChatInterface {
@@ -47,20 +57,26 @@ func NewChatInterface(model string, manager *ollama.Manager) *ChatInterface {
4757
input.Focus()
4858

4959
vp := viewport.New(80, 20)
50-
vp.KeyMap.PageDown.SetEnabled(false)
51-
vp.KeyMap.PageUp.SetEnabled(false)
60+
// vp.KeyMap.PageDown.SetEnabled(false)
61+
// vp.KeyMap.PageUp.SetEnabled(false)
5262

5363
s := spinner.New()
5464
s.Spinner = spinner.Dot
5565
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500"))
5666

67+
renderer, _ := glamour.NewTermRenderer(
68+
glamour.WithAutoStyle(),
69+
glamour.WithWordWrap(80),
70+
)
71+
5772
return &ChatInterface{
5873
model: model,
5974
manager: manager,
6075
messages: []string{},
6176
viewport: vp,
6277
input: input,
6378
spinner: s,
79+
renderer: renderer,
6480
}
6581
}
6682

@@ -73,6 +89,17 @@ func (c *ChatInterface) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
7389

7490
switch msg := msg.(type) {
7591
case tea.KeyMsg:
92+
if c.waiting {
93+
// Ignore most key presses while waiting
94+
switch msg.String() {
95+
case "ctrl+c":
96+
c.quitting = true
97+
return c, tea.Quit
98+
default:
99+
return c, nil
100+
}
101+
}
102+
76103
switch msg.String() {
77104
case "ctrl+c", "/exit":
78105
c.quitting = true
@@ -99,43 +126,75 @@ func (c *ChatInterface) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
99126
case userMessageMsg:
100127
c.addMessage("You", string(msg))
101128
c.waiting = true
129+
c.input.Blur()
102130
cmds = append(cmds, c.handleUserMessage(msg), c.spinner.Tick)
103131

104132
case aiResponseMsg:
105133
c.waiting = false
106134
c.addMessage(c.model, string(msg))
135+
c.input.Focus()
107136
}
108137

109138
if c.waiting {
110139
var cmd tea.Cmd
111140
c.spinner, cmd = c.spinner.Update(msg)
112141
cmds = append(cmds, cmd)
142+
} else {
143+
var cmd tea.Cmd
144+
c.input, cmd = c.input.Update(msg)
145+
cmds = append(cmds, cmd)
113146
}
114147

115-
c.input, _ = c.input.Update(msg)
148+
// c.input, _ = c.input.Update(msg)
149+
150+
var cmd tea.Cmd
151+
c.viewport, cmd = c.viewport.Update(msg)
152+
cmds = append(cmds, cmd)
116153

117154
return c, tea.Batch(cmds...)
118155
}
119156

157+
// func (c *ChatInterface) View() string {
158+
// var status string
159+
// var inputView string
160+
161+
// if c.waiting {
162+
// status = fmt.Sprintf("%s AI is thinking...", c.spinner.View())
163+
// inputView = disabledInputStyle.Render(c.input.View())
164+
// } else {
165+
// status = "Ready for your message"
166+
// inputView = activeInputStyle.Render(c.input.View())
167+
// }
168+
169+
// maxInputWidth := c.width - 4 // Adjust this value as needed
170+
// if len(inputView) > maxInputWidth && maxInputWidth > 0 {
171+
// inputView = inputView[:maxInputWidth] + "..."
172+
// }
173+
174+
// return fmt.Sprintf(
175+
// "%s\n%s\n%s",
176+
// c.viewport.View(),
177+
// inputView,
178+
// lipgloss.NewStyle().Foreground(lipgloss.Color("#98FB98")).Render(status),
179+
// )
180+
// }
181+
120182
func (c *ChatInterface) View() string {
121183
var status string
184+
var inputView string
185+
122186
if c.waiting {
123187
status = fmt.Sprintf("%s AI is thinking...", c.spinner.View())
188+
inputView = disabledInputStyle.Render(c.input.View())
124189
} else {
125190
status = "Ready for your message"
191+
inputView = activeInputStyle.Render(c.input.View())
126192
}
127193

128-
inputView := c.input.View()
129-
maxInputWidth := c.width - 4 // Adjust this value as needed
130-
if len(inputView) > maxInputWidth && maxInputWidth > 0 {
131-
inputView = inputView[:maxInputWidth] + "..."
132-
}
133-
134-
return fmt.Sprintf(
135-
"%s\n%s\n%s",
194+
return lipgloss.JoinVertical(lipgloss.Left,
136195
c.viewport.View(),
137-
inputStyle.Render(inputView),
138-
lipgloss.NewStyle().Foreground(lipgloss.Color("#98FB98")).Render(status),
196+
inputView,
197+
statusStyle.Render(status),
139198
)
140199
}
141200

@@ -161,52 +220,65 @@ func (c *ChatInterface) handleUserMessage(msg userMessageMsg) tea.Cmd {
161220
}
162221
}
163222

223+
// func (c *ChatInterface) addMessage(sender, content string) {
224+
// style := userStyle
225+
// if sender != "You" {
226+
// style = aiStyle
227+
// }
228+
// formattedMsg := style.Render(sender+":") + " " + content
229+
// wrappedMsg := c.wrapText(formattedMsg, c.width)
230+
// c.messages = append(c.messages, wrappedMsg)
231+
// c.updateViewportContent()
232+
// }
233+
164234
func (c *ChatInterface) addMessage(sender, content string) {
165-
style := userStyle
166-
if sender != "You" {
167-
style = aiStyle
235+
var formattedMsg string
236+
if sender == "You" {
237+
formattedMsg = userStyle.Render(sender+":") + " " + content
238+
} else {
239+
rendered, _ := c.renderer.Render(content)
240+
formattedMsg = aiStyle.Render(sender+":") + "\n" + rendered
168241
}
169-
formattedMsg := style.Render(sender+":") + " " + content
170-
wrappedMsg := c.wrapText(formattedMsg, c.width)
171-
c.messages = append(c.messages, wrappedMsg)
242+
c.messages = append(c.messages, formattedMsg)
172243
c.updateViewportContent()
173244
}
174245

175246
func (c *ChatInterface) updateViewportContent() {
176-
c.viewport.SetContent(strings.Join(c.messages, "\n\n"))
247+
content := strings.Join(c.messages, "\n\n")
248+
c.viewport.SetContent(content)
177249
c.viewport.GotoBottom()
178250
}
179251

180-
func (c *ChatInterface) wrapText(text string, width int) string {
181-
if width <= 0 {
182-
return text
183-
}
184-
words := strings.Fields(text)
185-
if len(words) == 0 {
186-
return text
187-
}
188-
189-
var lines []string
190-
var currentLine string
191-
192-
for _, word := range words {
193-
if len(currentLine)+len(word)+1 > width {
194-
lines = append(lines, strings.TrimSpace(currentLine))
195-
currentLine = word
196-
} else {
197-
if currentLine != "" {
198-
currentLine += " "
199-
}
200-
currentLine += word
201-
}
202-
}
203-
204-
if currentLine != "" {
205-
lines = append(lines, strings.TrimSpace(currentLine))
206-
}
207-
208-
return strings.Join(lines, "\n")
209-
}
252+
// func (c *ChatInterface) wrapText(text string, width int) string {
253+
// if width <= 0 {
254+
// return text
255+
// }
256+
// words := strings.Fields(text)
257+
// if len(words) == 0 {
258+
// return text
259+
// }
260+
261+
// var lines []string
262+
// var currentLine string
263+
264+
// for _, word := range words {
265+
// if len(currentLine)+len(word)+1 > width {
266+
// lines = append(lines, strings.TrimSpace(currentLine))
267+
// currentLine = word
268+
// } else {
269+
// if currentLine != "" {
270+
// currentLine += " "
271+
// }
272+
// currentLine += word
273+
// }
274+
// }
275+
276+
// if currentLine != "" {
277+
// lines = append(lines, strings.TrimSpace(currentLine))
278+
// }
279+
280+
// return strings.Join(lines, "\n")
281+
// }
210282

211283
func (c *ChatInterface) Run() (bool, error) {
212284
p := tea.NewProgram(c, tea.WithAltScreen())

0 commit comments

Comments
 (0)