markdown.go 8.7 KB

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