markdown.go 8.6 KB

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