|
| 1 | +# Glamour v2 Upgrade Guide |
| 2 | + |
| 3 | +This guide will help you migrate from Glamour v1 to v2. Most upgrades are straightforward and can be completed in minutes. |
| 4 | + |
| 5 | +## Update Import Paths |
| 6 | + |
| 7 | +**Required:** All imports must use the new `charm.land` module path with `/v2`: |
| 8 | + |
| 9 | +```diff |
| 10 | +-import "github.com/charmbracelet/glamour" |
| 11 | +-import "github.com/charmbracelet/glamour/ansi" |
| 12 | +-import "github.com/charmbracelet/glamour/styles" |
| 13 | ++import "charm.land/glamour/v2" |
| 14 | ++import "charm.land/glamour/v2/ansi" |
| 15 | ++import "charm.land/glamour/v2/styles" |
| 16 | +``` |
| 17 | + |
| 18 | +## Update Dependencies |
| 19 | + |
| 20 | +```bash |
| 21 | +go get charm.land/glamour/v2@latest |
| 22 | +``` |
| 23 | + |
| 24 | +If you need color downsampling (most apps do): |
| 25 | + |
| 26 | +```bash |
| 27 | +go get charm.land/lipgloss/v2@latest |
| 28 | +``` |
| 29 | + |
| 30 | +## Remove Auto Style Detection |
| 31 | + |
| 32 | +**Breaking:** `WithAutoStyle()` has been removed. The default style is now `"dark"`. |
| 33 | + |
| 34 | +```diff |
| 35 | +-r, _ := glamour.NewTermRenderer( |
| 36 | +- glamour.WithAutoStyle(), |
| 37 | +-) |
| 38 | ++r, _ := glamour.NewTermRenderer( |
| 39 | ++ // "dark" is the default, or specify explicitly |
| 40 | ++ glamour.WithStylePath("dark"), |
| 41 | ++) |
| 42 | +``` |
| 43 | + |
| 44 | +If you were relying on automatic style selection based on terminal background, you'll need to choose the style explicitly: |
| 45 | + |
| 46 | +```go |
| 47 | +// For light backgrounds |
| 48 | +r, _ := glamour.NewTermRenderer(glamour.WithStylePath("light")) |
| 49 | + |
| 50 | +// For dark backgrounds (default) |
| 51 | +r, _ := glamour.NewTermRenderer(glamour.WithStylePath("dark")) |
| 52 | + |
| 53 | +// Other built-in styles: "pink", "dracula", "tokyo-night", "ascii" |
| 54 | +r, _ := glamour.NewTermRenderer(glamour.WithStylePath("dracula")) |
| 55 | +``` |
| 56 | + |
| 57 | +Want to detect the terminal background yourself? You can use Lip Gloss: |
| 58 | + |
| 59 | +```go |
| 60 | +import "charm.land/lipgloss/v2" |
| 61 | + |
| 62 | +// Detect if we're on a dark background |
| 63 | +isDark := lipgloss.HasDarkBackground() |
| 64 | + |
| 65 | +style := "dark" |
| 66 | +if !isDark { |
| 67 | + style = "light" |
| 68 | +} |
| 69 | + |
| 70 | +r, _ := glamour.NewTermRenderer(glamour.WithStylePath(style)) |
| 71 | +``` |
| 72 | + |
| 73 | +## Handle Color Downsampling Explicitly |
| 74 | + |
| 75 | +**Breaking:** `WithColorProfile()` has been removed. Use Lip Gloss for color adaptation: |
| 76 | + |
| 77 | +```diff |
| 78 | +-import "github.com/muesli/termenv" |
| 79 | +- |
| 80 | +-r, _ := glamour.NewTermRenderer( |
| 81 | +- glamour.WithColorProfile(termenv.TrueColor), |
| 82 | +-) |
| 83 | +-out, _ := r.Render(markdown) |
| 84 | +-fmt.Print(out) |
| 85 | ++import "charm.land/lipgloss/v2" |
| 86 | ++ |
| 87 | ++r, _ := glamour.NewTermRenderer( |
| 88 | ++ glamour.WithWordWrap(80), |
| 89 | ++) |
| 90 | ++out, _ := r.Render(markdown) |
| 91 | ++ |
| 92 | ++// Lip Gloss handles color downsampling based on terminal capabilities |
| 93 | ++lipgloss.Print(out) |
| 94 | +``` |
| 95 | + |
| 96 | +Why the change? Glamour is now pure — it always produces the same output for the same input. This makes it more predictable and testable. Lip Gloss handles the terminal-specific color adaptation when you're ready to display the output. |
| 97 | + |
| 98 | +If you don't need color adaptation (e.g., you know you're always outputting TrueColor): |
| 99 | + |
| 100 | +```go |
| 101 | +r, _ := glamour.NewTermRenderer(glamour.WithWordWrap(80)) |
| 102 | +out, _ := r.Render(markdown) |
| 103 | +fmt.Print(out) // Direct output, no downsampling |
| 104 | +``` |
| 105 | + |
| 106 | +## Remove Overline Styles |
| 107 | + |
| 108 | +**Breaking:** The `Overlined` field has been removed from style configurations. |
| 109 | + |
| 110 | +If you have custom styles using `Overlined`: |
| 111 | + |
| 112 | +```diff |
| 113 | + StylePrimitive: ansi.StylePrimitive{ |
| 114 | + Bold: &trueBool, |
| 115 | + Underline: &trueBool, |
| 116 | +- Overlined: &trueBool, |
| 117 | + } |
| 118 | +``` |
| 119 | + |
| 120 | +Overline was rarely supported across terminals and not widely used. If you need similar visual separation, consider alternatives like underline, bold, inverse, or background colors. |
| 121 | + |
| 122 | +## Update Custom Style Definitions |
| 123 | + |
| 124 | +If you maintain custom `StyleConfig` definitions, update the import paths: |
| 125 | + |
| 126 | +```diff |
| 127 | +-import "github.com/charmbracelet/glamour/ansi" |
| 128 | ++import "charm.land/glamour/v2/ansi" |
| 129 | + |
| 130 | + var myStyle = &ansi.StyleConfig{ |
| 131 | + // Your custom style definition |
| 132 | + Document: ansi.StyleBlock{ |
| 133 | + StylePrimitive: ansi.StylePrimitive{ |
| 134 | + Color: stringPtr("#E6DB74"), |
| 135 | + }, |
| 136 | + }, |
| 137 | + } |
| 138 | +``` |
| 139 | + |
| 140 | +The structure is the same; only the import path changes. |
| 141 | + |
| 142 | +## Verify Custom Writers (Advanced) |
| 143 | + |
| 144 | +If you implemented custom margin or padding writers using `ansi.MarginWriter`: |
| 145 | + |
| 146 | +1. Ensure you call `.Close()` on all writer instances |
| 147 | +2. The new `IndentWriter` and `PaddingWriter` types are available for custom use |
| 148 | + |
| 149 | +```go |
| 150 | +import "charm.land/glamour/v2/ansi" |
| 151 | + |
| 152 | +mw := ansi.NewMarginWriter(ctx, w, style) |
| 153 | +defer mw.Close() // Important: always close writers now |
| 154 | + |
| 155 | +// Write your content |
| 156 | +io.WriteString(mw, content) |
| 157 | +``` |
| 158 | + |
| 159 | +This improves memory management and prevents resource leaks. |
| 160 | + |
| 161 | +## Example Migration |
| 162 | + |
| 163 | +Here's a complete before/after example: |
| 164 | + |
| 165 | +### Before (v1) |
| 166 | + |
| 167 | +```go |
| 168 | +package main |
| 169 | + |
| 170 | +import ( |
| 171 | + "fmt" |
| 172 | + "github.com/charmbracelet/glamour" |
| 173 | + "github.com/muesli/termenv" |
| 174 | +) |
| 175 | + |
| 176 | +func main() { |
| 177 | + md := `# Hello World |
| 178 | +
|
| 179 | +This is **Glamour v1**! |
| 180 | +` |
| 181 | + |
| 182 | + r, _ := glamour.NewTermRenderer( |
| 183 | + glamour.WithAutoStyle(), |
| 184 | + glamour.WithColorProfile(termenv.TrueColor), |
| 185 | + glamour.WithWordWrap(80), |
| 186 | + ) |
| 187 | + |
| 188 | + out, _ := r.Render(md) |
| 189 | + fmt.Print(out) |
| 190 | +} |
| 191 | +``` |
| 192 | + |
| 193 | +### After (v2) |
| 194 | + |
| 195 | +```go |
| 196 | +package main |
| 197 | + |
| 198 | +import ( |
| 199 | + "fmt" |
| 200 | + "charm.land/glamour/v2" |
| 201 | + "charm.land/lipgloss/v2" |
| 202 | +) |
| 203 | + |
| 204 | +func main() { |
| 205 | + md := `# Hello World |
| 206 | +
|
| 207 | +This is **Glamour v2**! |
| 208 | +` |
| 209 | + |
| 210 | + r, _ := glamour.NewTermRenderer( |
| 211 | + glamour.WithStylePath("dark"), // or omit for default |
| 212 | + glamour.WithWordWrap(80), |
| 213 | + ) |
| 214 | + |
| 215 | + out, _ := r.Render(md) |
| 216 | + lipgloss.Print(out) // Handles color downsampling |
| 217 | +} |
| 218 | +``` |
| 219 | + |
| 220 | +## Testing Your Migration |
| 221 | + |
| 222 | +After making changes: |
| 223 | + |
| 224 | +1. **Run your tests** — Ensure all tests pass with the new version |
| 225 | +2. **Visual check** — Render sample markdown and verify output looks correct |
| 226 | +3. **Check wrapping** — Pay special attention to text wrapping if you have CJK or emoji content (it should be better now!) |
| 227 | +4. **Test hyperlinks** — If you use autolinks, verify they render correctly |
| 228 | + |
| 229 | +## Common Issues |
| 230 | + |
| 231 | +### "cannot find package" |
| 232 | + |
| 233 | +Make sure you've updated your `go.mod`: |
| 234 | + |
| 235 | +```bash |
| 236 | +go get charm.land/glamour/v2 |
| 237 | +``` |
| 238 | + |
| 239 | +And that all imports use the new path with `/v2`. |
| 240 | + |
| 241 | +### Colors look wrong |
| 242 | + |
| 243 | +If colors aren't displaying correctly, make sure you're using `lipgloss.Print()` instead of `fmt.Print()`: |
| 244 | + |
| 245 | +```go |
| 246 | +out, _ := r.Render(markdown) |
| 247 | +lipgloss.Print(out) // Not fmt.Print(out) |
| 248 | +``` |
| 249 | + |
| 250 | +### Text wrapping issues |
| 251 | + |
| 252 | +Glamour v2 has improved text wrapping, especially for CJK characters and emojis. If you're seeing wrapping issues, it's likely a regression. Please [open an issue](https://github.com/charmbracelet/glamour/issues)! |
| 253 | + |
| 254 | +## Need Help? |
| 255 | + |
| 256 | +If you run into issues during migration: |
| 257 | + |
| 258 | +- Check the [examples directory](https://github.com/charmbracelet/glamour/tree/main/examples) for working code |
| 259 | +- Review [What's New](WHATS_NEW.md) for detailed feature changes |
| 260 | +- Join us on [Discord](https://charm.sh/chat) |
| 261 | +- Open an issue on [GitHub](https://github.com/charmbracelet/glamour/issues) |
| 262 | + |
| 263 | +## Feedback |
| 264 | + |
| 265 | +Migrated successfully? Having trouble? We'd love to hear about it! |
| 266 | + |
| 267 | +- [Discord](https://charm.sh/chat) |
| 268 | +- [The Fediverse](https://mastodon.social/@charmcli) |
| 269 | +- [Twitter](https://twitter.com/charmcli) |
| 270 | + |
| 271 | +--- |
| 272 | + |
| 273 | +Welcome to Glamour v2! 💄 |
| 274 | + |
| 275 | +Part of [Charm](https://charm.sh). |
| 276 | + |
| 277 | +<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a> |
| 278 | + |
| 279 | +Charm热爱开源 • Charm loves open source |
0 commit comments