markdown.go 10 KB

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