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