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