1package styles
2
3import (
4 "github.com/charmbracelet/glamour"
5 "github.com/charmbracelet/glamour/ansi"
6 "github.com/charmbracelet/lipgloss"
7 "github.com/opencode-ai/opencode/internal/tui/theme"
8)
9
10const defaultMargin = 1
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 BlockPrefix: "",
35 BlockSuffix: "",
36 Color: stringPtr(adaptiveColorToString(t.MarkdownText())),
37 },
38 Margin: uintPtr(defaultMargin),
39 },
40 BlockQuote: ansi.StyleBlock{
41 StylePrimitive: ansi.StylePrimitive{
42 Color: stringPtr(adaptiveColorToString(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(adaptiveColorToString(t.MarkdownText())),
55 },
56 },
57 },
58 Heading: ansi.StyleBlock{
59 StylePrimitive: ansi.StylePrimitive{
60 BlockSuffix: "\n",
61 Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
62 Bold: boolPtr(true),
63 },
64 },
65 H1: ansi.StyleBlock{
66 StylePrimitive: ansi.StylePrimitive{
67 Prefix: "# ",
68 Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
69 Bold: boolPtr(true),
70 },
71 },
72 H2: ansi.StyleBlock{
73 StylePrimitive: ansi.StylePrimitive{
74 Prefix: "## ",
75 Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
76 Bold: boolPtr(true),
77 },
78 },
79 H3: ansi.StyleBlock{
80 StylePrimitive: ansi.StylePrimitive{
81 Prefix: "### ",
82 Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
83 Bold: boolPtr(true),
84 },
85 },
86 H4: ansi.StyleBlock{
87 StylePrimitive: ansi.StylePrimitive{
88 Prefix: "#### ",
89 Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
90 Bold: boolPtr(true),
91 },
92 },
93 H5: ansi.StyleBlock{
94 StylePrimitive: ansi.StylePrimitive{
95 Prefix: "##### ",
96 Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
97 Bold: boolPtr(true),
98 },
99 },
100 H6: ansi.StyleBlock{
101 StylePrimitive: ansi.StylePrimitive{
102 Prefix: "###### ",
103 Color: stringPtr(adaptiveColorToString(t.MarkdownHeading())),
104 Bold: boolPtr(true),
105 },
106 },
107 Strikethrough: ansi.StylePrimitive{
108 CrossedOut: boolPtr(true),
109 Color: stringPtr(adaptiveColorToString(t.TextMuted())),
110 },
111 Emph: ansi.StylePrimitive{
112 Color: stringPtr(adaptiveColorToString(t.MarkdownEmph())),
113 Italic: boolPtr(true),
114 },
115 Strong: ansi.StylePrimitive{
116 Bold: boolPtr(true),
117 Color: stringPtr(adaptiveColorToString(t.MarkdownStrong())),
118 },
119 HorizontalRule: ansi.StylePrimitive{
120 Color: stringPtr(adaptiveColorToString(t.MarkdownHorizontalRule())),
121 Format: "\nāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n",
122 },
123 Item: ansi.StylePrimitive{
124 BlockPrefix: "⢠",
125 Color: stringPtr(adaptiveColorToString(t.MarkdownListItem())),
126 },
127 Enumeration: ansi.StylePrimitive{
128 BlockPrefix: ". ",
129 Color: stringPtr(adaptiveColorToString(t.MarkdownListEnumeration())),
130 },
131 Task: ansi.StyleTask{
132 StylePrimitive: ansi.StylePrimitive{},
133 Ticked: "[ā] ",
134 Unticked: "[ ] ",
135 },
136 Link: ansi.StylePrimitive{
137 Color: stringPtr(adaptiveColorToString(t.MarkdownLink())),
138 Underline: boolPtr(true),
139 },
140 LinkText: ansi.StylePrimitive{
141 Color: stringPtr(adaptiveColorToString(t.MarkdownLinkText())),
142 Bold: boolPtr(true),
143 },
144 Image: ansi.StylePrimitive{
145 Color: stringPtr(adaptiveColorToString(t.MarkdownImage())),
146 Underline: boolPtr(true),
147 Format: "š¼ {{.text}}",
148 },
149 ImageText: ansi.StylePrimitive{
150 Color: stringPtr(adaptiveColorToString(t.MarkdownImageText())),
151 Format: "{{.text}}",
152 },
153 Code: ansi.StyleBlock{
154 StylePrimitive: ansi.StylePrimitive{
155 Color: stringPtr(adaptiveColorToString(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(adaptiveColorToString(t.MarkdownCodeBlock())),
165 },
166 Margin: uintPtr(defaultMargin),
167 },
168 Chroma: &ansi.Chroma{
169 Text: ansi.StylePrimitive{
170 Color: stringPtr(adaptiveColorToString(t.MarkdownText())),
171 },
172 Error: ansi.StylePrimitive{
173 Color: stringPtr(adaptiveColorToString(t.Error())),
174 },
175 Comment: ansi.StylePrimitive{
176 Color: stringPtr(adaptiveColorToString(t.SyntaxComment())),
177 },
178 CommentPreproc: ansi.StylePrimitive{
179 Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
180 },
181 Keyword: ansi.StylePrimitive{
182 Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
183 },
184 KeywordReserved: ansi.StylePrimitive{
185 Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
186 },
187 KeywordNamespace: ansi.StylePrimitive{
188 Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
189 },
190 KeywordType: ansi.StylePrimitive{
191 Color: stringPtr(adaptiveColorToString(t.SyntaxType())),
192 },
193 Operator: ansi.StylePrimitive{
194 Color: stringPtr(adaptiveColorToString(t.SyntaxOperator())),
195 },
196 Punctuation: ansi.StylePrimitive{
197 Color: stringPtr(adaptiveColorToString(t.SyntaxPunctuation())),
198 },
199 Name: ansi.StylePrimitive{
200 Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())),
201 },
202 NameBuiltin: ansi.StylePrimitive{
203 Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())),
204 },
205 NameTag: ansi.StylePrimitive{
206 Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
207 },
208 NameAttribute: ansi.StylePrimitive{
209 Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())),
210 },
211 NameClass: ansi.StylePrimitive{
212 Color: stringPtr(adaptiveColorToString(t.SyntaxType())),
213 },
214 NameConstant: ansi.StylePrimitive{
215 Color: stringPtr(adaptiveColorToString(t.SyntaxVariable())),
216 },
217 NameDecorator: ansi.StylePrimitive{
218 Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())),
219 },
220 NameFunction: ansi.StylePrimitive{
221 Color: stringPtr(adaptiveColorToString(t.SyntaxFunction())),
222 },
223 LiteralNumber: ansi.StylePrimitive{
224 Color: stringPtr(adaptiveColorToString(t.SyntaxNumber())),
225 },
226 LiteralString: ansi.StylePrimitive{
227 Color: stringPtr(adaptiveColorToString(t.SyntaxString())),
228 },
229 LiteralStringEscape: ansi.StylePrimitive{
230 Color: stringPtr(adaptiveColorToString(t.SyntaxKeyword())),
231 },
232 GenericDeleted: ansi.StylePrimitive{
233 Color: stringPtr(adaptiveColorToString(t.DiffRemoved())),
234 },
235 GenericEmph: ansi.StylePrimitive{
236 Color: stringPtr(adaptiveColorToString(t.MarkdownEmph())),
237 Italic: boolPtr(true),
238 },
239 GenericInserted: ansi.StylePrimitive{
240 Color: stringPtr(adaptiveColorToString(t.DiffAdded())),
241 },
242 GenericStrong: ansi.StylePrimitive{
243 Color: stringPtr(adaptiveColorToString(t.MarkdownStrong())),
244 Bold: boolPtr(true),
245 },
246 GenericSubheading: ansi.StylePrimitive{
247 Color: stringPtr(adaptiveColorToString(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(adaptiveColorToString(t.MarkdownLinkText())),
265 },
266 Text: ansi.StylePrimitive{
267 Color: stringPtr(adaptiveColorToString(t.MarkdownText())),
268 },
269 Paragraph: ansi.StyleBlock{
270 StylePrimitive: ansi.StylePrimitive{
271 Color: stringPtr(adaptiveColorToString(t.MarkdownText())),
272 },
273 },
274 }
275}
276
277// adaptiveColorToString converts a lipgloss.AdaptiveColor to the appropriate
278// hex color string based on the current terminal background
279func adaptiveColorToString(color lipgloss.AdaptiveColor) string {
280 if lipgloss.HasDarkBackground() {
281 return color.Dark
282 }
283 return color.Light
284}