1package styles
2
3import (
4 "fmt"
5 "image/color"
6
7 "github.com/charmbracelet/glamour/v2"
8 "github.com/charmbracelet/glamour/v2/ansi"
9 "github.com/opencode-ai/opencode/internal/tui/theme"
10)
11
12const defaultMargin = 1
13
14// Helper functions for style pointers
15func boolPtr(b bool) *bool { return &b }
16func stringPtr(s string) *string { return &s }
17func uintPtr(u uint) *uint { return &u }
18
19// returns a glamour TermRenderer configured with the current theme
20func GetMarkdownRenderer(width int) *glamour.TermRenderer {
21 r, _ := glamour.NewTermRenderer(
22 glamour.WithStyles(generateMarkdownStyleConfig()),
23 glamour.WithWordWrap(width),
24 )
25 return r
26}
27
28// creates an ansi.StyleConfig for markdown rendering
29// using adaptive colors from the provided theme.
30func generateMarkdownStyleConfig() ansi.StyleConfig {
31 t := theme.CurrentTheme()
32
33 return ansi.StyleConfig{
34 Document: ansi.StyleBlock{
35 StylePrimitive: ansi.StylePrimitive{
36 Color: stringPtr(colorToString(t.MarkdownText())),
37 },
38 Margin: uintPtr(defaultMargin),
39 },
40 BlockQuote: ansi.StyleBlock{
41 StylePrimitive: ansi.StylePrimitive{
42 Color: stringPtr(colorToString(t.MarkdownBlockQuote())),
43 Italic: boolPtr(true),
44 Prefix: "ā ",
45 },
46 Indent: uintPtr(1),
47 IndentToken: stringPtr(BaseStyle().Render(" ")),
48 },
49 List: ansi.StyleList{
50 LevelIndent: defaultMargin,
51 StyleBlock: ansi.StyleBlock{
52 IndentToken: stringPtr(BaseStyle().Render(" ")),
53 StylePrimitive: ansi.StylePrimitive{
54 Color: stringPtr(colorToString(t.MarkdownText())),
55 },
56 },
57 },
58 Heading: ansi.StyleBlock{
59 StylePrimitive: ansi.StylePrimitive{
60 BlockSuffix: "\n",
61 Color: stringPtr(colorToString(t.MarkdownHeading())),
62 Bold: boolPtr(true),
63 },
64 },
65 H1: ansi.StyleBlock{
66 StylePrimitive: ansi.StylePrimitive{
67 Prefix: "# ",
68 Color: stringPtr(colorToString(t.MarkdownHeading())),
69 Bold: boolPtr(true),
70 },
71 },
72 H2: ansi.StyleBlock{
73 StylePrimitive: ansi.StylePrimitive{
74 Prefix: "## ",
75 Color: stringPtr(colorToString(t.MarkdownHeading())),
76 Bold: boolPtr(true),
77 },
78 },
79 H3: ansi.StyleBlock{
80 StylePrimitive: ansi.StylePrimitive{
81 Prefix: "### ",
82 Color: stringPtr(colorToString(t.MarkdownHeading())),
83 Bold: boolPtr(true),
84 },
85 },
86 H4: ansi.StyleBlock{
87 StylePrimitive: ansi.StylePrimitive{
88 Prefix: "#### ",
89 Color: stringPtr(colorToString(t.MarkdownHeading())),
90 Bold: boolPtr(true),
91 },
92 },
93 H5: ansi.StyleBlock{
94 StylePrimitive: ansi.StylePrimitive{
95 Prefix: "##### ",
96 Color: stringPtr(colorToString(t.MarkdownHeading())),
97 Bold: boolPtr(true),
98 },
99 },
100 H6: ansi.StyleBlock{
101 StylePrimitive: ansi.StylePrimitive{
102 Prefix: "###### ",
103 Color: stringPtr(colorToString(t.MarkdownHeading())),
104 Bold: boolPtr(true),
105 },
106 },
107 Strikethrough: ansi.StylePrimitive{
108 CrossedOut: boolPtr(true),
109 Color: stringPtr(colorToString(t.TextMuted())),
110 },
111 Emph: ansi.StylePrimitive{
112 Color: stringPtr(colorToString(t.MarkdownEmph())),
113 Italic: boolPtr(true),
114 },
115 Strong: ansi.StylePrimitive{
116 Bold: boolPtr(true),
117 Color: stringPtr(colorToString(t.MarkdownStrong())),
118 },
119 HorizontalRule: ansi.StylePrimitive{
120 Color: stringPtr(colorToString(t.MarkdownHorizontalRule())),
121 Format: "\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n",
122 },
123 Item: ansi.StylePrimitive{
124 BlockPrefix: "⢠",
125 Color: stringPtr(colorToString(t.MarkdownListItem())),
126 },
127 Enumeration: ansi.StylePrimitive{
128 BlockPrefix: ". ",
129 Color: stringPtr(colorToString(t.MarkdownListEnumeration())),
130 },
131 Task: ansi.StyleTask{
132 StylePrimitive: ansi.StylePrimitive{},
133 Ticked: "[ā] ",
134 Unticked: "[ ] ",
135 },
136 Link: ansi.StylePrimitive{
137 Color: stringPtr(colorToString(t.MarkdownLink())),
138 Underline: boolPtr(true),
139 },
140 LinkText: ansi.StylePrimitive{
141 Color: stringPtr(colorToString(t.MarkdownLinkText())),
142 Bold: boolPtr(true),
143 },
144 Image: ansi.StylePrimitive{
145 Color: stringPtr(colorToString(t.MarkdownImage())),
146 Underline: boolPtr(true),
147 Format: "š¼ {{.text}}",
148 },
149 ImageText: ansi.StylePrimitive{
150 Color: stringPtr(colorToString(t.MarkdownImageText())),
151 Format: "{{.text}}",
152 },
153 Code: ansi.StyleBlock{
154 StylePrimitive: ansi.StylePrimitive{
155 Color: stringPtr(colorToString(t.MarkdownCode())),
156 Prefix: "",
157 Suffix: "",
158 },
159 },
160 CodeBlock: ansi.StyleCodeBlock{
161 StyleBlock: ansi.StyleBlock{
162 StylePrimitive: ansi.StylePrimitive{
163 Prefix: " ",
164 Color: stringPtr(colorToString(t.MarkdownCodeBlock())),
165 },
166 Margin: uintPtr(defaultMargin),
167 },
168 Chroma: &ansi.Chroma{
169 Text: ansi.StylePrimitive{
170 Color: stringPtr(colorToString(t.MarkdownText())),
171 },
172 Error: ansi.StylePrimitive{
173 Color: stringPtr(colorToString(t.Error())),
174 },
175 Comment: ansi.StylePrimitive{
176 Color: stringPtr(colorToString(t.SyntaxComment())),
177 },
178 CommentPreproc: ansi.StylePrimitive{
179 Color: stringPtr(colorToString(t.SyntaxKeyword())),
180 },
181 Keyword: ansi.StylePrimitive{
182 Color: stringPtr(colorToString(t.SyntaxKeyword())),
183 },
184 KeywordReserved: ansi.StylePrimitive{
185 Color: stringPtr(colorToString(t.SyntaxKeyword())),
186 },
187 KeywordNamespace: ansi.StylePrimitive{
188 Color: stringPtr(colorToString(t.SyntaxKeyword())),
189 },
190 KeywordType: ansi.StylePrimitive{
191 Color: stringPtr(colorToString(t.SyntaxType())),
192 },
193 Operator: ansi.StylePrimitive{
194 Color: stringPtr(colorToString(t.SyntaxOperator())),
195 },
196 Punctuation: ansi.StylePrimitive{
197 Color: stringPtr(colorToString(t.SyntaxPunctuation())),
198 },
199 Name: ansi.StylePrimitive{
200 Color: stringPtr(colorToString(t.SyntaxVariable())),
201 },
202 NameBuiltin: ansi.StylePrimitive{
203 Color: stringPtr(colorToString(t.SyntaxVariable())),
204 },
205 NameTag: ansi.StylePrimitive{
206 Color: stringPtr(colorToString(t.SyntaxKeyword())),
207 },
208 NameAttribute: ansi.StylePrimitive{
209 Color: stringPtr(colorToString(t.SyntaxFunction())),
210 },
211 NameClass: ansi.StylePrimitive{
212 Color: stringPtr(colorToString(t.SyntaxType())),
213 },
214 NameConstant: ansi.StylePrimitive{
215 Color: stringPtr(colorToString(t.SyntaxVariable())),
216 },
217 NameDecorator: ansi.StylePrimitive{
218 Color: stringPtr(colorToString(t.SyntaxFunction())),
219 },
220 NameFunction: ansi.StylePrimitive{
221 Color: stringPtr(colorToString(t.SyntaxFunction())),
222 },
223 LiteralNumber: ansi.StylePrimitive{
224 Color: stringPtr(colorToString(t.SyntaxNumber())),
225 },
226 LiteralString: ansi.StylePrimitive{
227 Color: stringPtr(colorToString(t.SyntaxString())),
228 },
229 LiteralStringEscape: ansi.StylePrimitive{
230 Color: stringPtr(colorToString(t.SyntaxKeyword())),
231 },
232 GenericDeleted: ansi.StylePrimitive{
233 Color: stringPtr(colorToString(t.DiffRemoved())),
234 },
235 GenericEmph: ansi.StylePrimitive{
236 Color: stringPtr(colorToString(t.MarkdownEmph())),
237 Italic: boolPtr(true),
238 },
239 GenericInserted: ansi.StylePrimitive{
240 Color: stringPtr(colorToString(t.DiffAdded())),
241 },
242 GenericStrong: ansi.StylePrimitive{
243 Color: stringPtr(colorToString(t.MarkdownStrong())),
244 Bold: boolPtr(true),
245 },
246 GenericSubheading: ansi.StylePrimitive{
247 Color: stringPtr(colorToString(t.MarkdownHeading())),
248 },
249 },
250 },
251 Table: ansi.StyleTable{
252 StyleBlock: ansi.StyleBlock{
253 StylePrimitive: ansi.StylePrimitive{
254 BlockPrefix: "\n",
255 BlockSuffix: "\n",
256 },
257 },
258 CenterSeparator: stringPtr("ā¼"),
259 ColumnSeparator: stringPtr("ā"),
260 RowSeparator: stringPtr("ā"),
261 },
262 DefinitionDescription: ansi.StylePrimitive{
263 BlockPrefix: "\n ⯠",
264 Color: stringPtr(colorToString(t.MarkdownLinkText())),
265 },
266 Text: ansi.StylePrimitive{
267 Color: stringPtr(colorToString(t.MarkdownText())),
268 },
269 Paragraph: ansi.StyleBlock{
270 StylePrimitive: ansi.StylePrimitive{
271 Color: stringPtr(colorToString(t.MarkdownText())),
272 },
273 },
274 }
275}
276
277func colorToString(c color.Color) string {
278 rgba := color.RGBAModel.Convert(c).(color.RGBA)
279 return fmt.Sprintf("#%02x%02x%02x", rgba.R, rgba.G, rgba.B)
280}