-
Notifications
You must be signed in to change notification settings - Fork 7
/
markdown.go
143 lines (113 loc) · 2.91 KB
/
markdown.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Package markdown provides an markdown bubble which can render
// markdown in a pretty manor.
package markdown
import (
"errors"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/glamour"
"github.com/charmbracelet/lipgloss"
"github.com/mistakenelf/teacup/filesystem"
)
type renderMarkdownMsg string
type errorMsg error
// Model represents the properties of a code bubble.
type Model struct {
Viewport viewport.Model
Active bool
FileName string
}
// RenderMarkdown renders the markdown content with glamour.
func RenderMarkdown(width int, content string) (string, error) {
background := "light"
if lipgloss.HasDarkBackground() {
background = "dark"
}
r, _ := glamour.NewTermRenderer(
glamour.WithWordWrap(width),
glamour.WithStandardStyle(background),
)
out, err := r.Render(content)
if err != nil {
return "", errors.Unwrap(err)
}
return out, nil
}
// renderMarkdownCmd renders text as pretty markdown.
func renderMarkdownCmd(width int, filename string) tea.Cmd {
return func() tea.Msg {
content, err := filesystem.ReadFileContent(filename)
if err != nil {
return errorMsg(err)
}
markdownContent, err := RenderMarkdown(width, content)
if err != nil {
return errorMsg(err)
}
return renderMarkdownMsg(markdownContent)
}
}
// New creates a new instance of markdown.
func New(active bool) Model {
viewPort := viewport.New(0, 0)
return Model{
Viewport: viewPort,
Active: active,
}
}
// Init initializes the code bubble.
func (m Model) Init() tea.Cmd {
return nil
}
// SetFileName sets current file to render, this
// returns a cmd which will render the text.
func (m *Model) SetFileName(filename string) tea.Cmd {
m.FileName = filename
return renderMarkdownCmd(m.Viewport.Width, filename)
}
// SetSize sets the size of the bubble.
func (m *Model) SetSize(w, h int) tea.Cmd {
m.Viewport.Width = w
m.Viewport.Height = h
if m.FileName != "" {
return renderMarkdownCmd(m.Viewport.Width, m.FileName)
}
return nil
}
// GotoTop jumps to the top of the viewport.
func (m *Model) GotoTop() {
m.Viewport.GotoTop()
}
// SetIsActive sets if the bubble is currently active.
func (m *Model) SetIsActive(active bool) {
m.Active = active
}
// Update handles updating the UI of a code bubble.
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
var (
cmd tea.Cmd
cmds []tea.Cmd
)
switch msg := msg.(type) {
case renderMarkdownMsg:
content := lipgloss.NewStyle().
Width(m.Viewport.Width).
Height(m.Viewport.Height).
Render(string(msg))
m.Viewport.SetContent(content)
return m, nil
case errorMsg:
m.FileName = ""
m.Viewport.SetContent(msg.Error())
return m, nil
}
if m.Active {
m.Viewport, cmd = m.Viewport.Update(msg)
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
}
// View returns a string representation of the markdown bubble.
func (m Model) View() string {
return m.Viewport.View()
}