renderMarkdown renders Markdown to ANSI using the ACTIVE theme. Colors for headings, links, inline code and fenced code blocks (chroma syntax tokens) all derive from the same palette as the cards and borders, so a theme swap re-skins markdown in lock-step. The WHOLE document is rendered in a single
(input string)
| 33 | // each block and replacing it with the styled "▌ lang" line post-render — so |
| 34 | // the chip never costs us the correctness of whole-document rendering. |
| 35 | func (cli *ChatCLI) renderMarkdown(input string) string { |
| 36 | prof := theme.ActiveProfile() |
| 37 | renderer, err := glamour.NewTermRenderer(theme.Active().GlamourOptions(prof)...) |
| 38 | if err != nil { |
| 39 | return input |
| 40 | } |
| 41 | |
| 42 | src, hasChips := injectChipSentinels(input) |
| 43 | out, rerr := renderer.Render(src) |
| 44 | if rerr != nil { |
| 45 | return input |
| 46 | } |
| 47 | if hasChips { |
| 48 | out = replaceChipSentinels(out, prof) |
| 49 | } |
| 50 | |
| 51 | out = strings.TrimRight(out, " \n\t") |
| 52 | if !strings.HasSuffix(out, "\033[0m") { |
| 53 | out += "\033[0m" |
| 54 | } |
| 55 | return out |
| 56 | } |
| 57 | |
| 58 | // injectChipSentinels rewrites the source so each top-level fenced code block |
| 59 | // with a declared language is preceded by a sentinel paragraph. Returns the |