CtrlK
BlogDocsLog inGet started
Tessl Logo

tpscloud/charmedimsure

Source-verified skills for the Charm terminal stack: Bubble Tea v2, Bubbles v2, Lip Gloss v2, Huh v2, Log v2, Glamour v2, plus the gum and vhs CLIs.

100

1.02x
Quality

100%

Does it follow best practices?

Impact

100%

1.02x

Average score across 9 eval scenarios

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

SKILL.mdskills/charmed-glamour/

name:
charmed-glamour
description:
Render Markdown to styled terminal text in Go with Glamour v2 (charm.land/glamour/v2, formerly github.com/charmbracelet/glamour): one shot Render, reusable TermRenderer with word wrap and emoji, built in styles (dark, light, dracula, tokyo-night, pink, ascii), custom StyleConfig in Go or JSON, color downsampling through Lip Gloss, and a scrolling viewer inside Bubble Tea. Use when showing a README, .md file, help text, changelog, or any Markdown in a CLI or TUI, or when asked to pretty-print Markdown in the terminal. Not for editing Markdown or for general styling (charmed-lipgloss).
license:
MIT

Glamour v2

Import as "charm.land/glamour/v2"; styles live in "charm.land/glamour/v2/styles" and the config types in "charm.land/glamour/v2/ansi". Pinned and verified against v2.0.1. Go blocks headed // examples/... are trimmed from complete programs that build against the pinned modules; // excerpt blocks are API sketches.

If the project's go.mod still requires github.com/charmbracelet/glamour (pre-v2), the v2 APIs here do not apply. Use go doc against the project's version and keep its import paths unless the user asks to migrate.

Workflow

  1. Pick "dark" or "light": outside Bubble Tea call lipgloss.HasDarkBackground(os.Stdin, os.Stdout); inside, read tea.BackgroundColorMsg.
  2. Build one renderer with glamour.NewTermRenderer(glamour.WithStandardStyle(style), glamour.WithWordWrap(n)) and reuse it.
  3. Render the Markdown with r.Render(md) and handle the error.
  4. Print with lipgloss.Print outside Bubble Tea; inside, hand the string to a viewport or the view.
  5. Check in a real terminal: light and dark styles, narrow widths, tables, code blocks, and links. For wrong colors or unexpected wrapping, see Mistakes.

Rendering

See the complete render example:

// examples/charmed-glamour/render/main.go
out, err := glamour.Render(doc, "dark") // one shot with a named style
if err != nil {
    fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
}
lipgloss.Print(out)

style := "dark"
if !lipgloss.HasDarkBackground(os.Stdin, os.Stdout) {
    style = "light"
}
r, err := glamour.NewTermRenderer(
    glamour.WithStandardStyle(style),
    glamour.WithWordWrap(60),
    glamour.WithEmoji(),
)
if err != nil { // unknown style name or unreadable style path
    fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
}
out, err = r.Render("Rendered again at 60 columns :tada:")
if err != nil {
    fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
}
lipgloss.Print(out)

The options used most: WithWordWrap(n) (default 80; 0 disables), WithEmoji() (:tada: becomes the character), and WithStylePath(s) (a name or a path to a JSON file).

Read references/options.md for the other built in style names, every renderer option, GLAMOUR_STYLE environment configuration, RenderBytes, and the buffered Write and Close API.

Custom styles

A style is an ansi.StyleConfig. Start from a built in config and edit, as in the custom style example:

// examples/charmed-glamour/custom/main.go
cfg := styles.DraculaStyleConfig // a value copy; safe to edit

cfg.Document.Margin = new(uint(0)) // new(value) needs Go 1.26 or later
cfg.H1.Prefix = ""
cfg.H1.BackgroundColor = new("#5A2D82")
cfg.H1.Color = new("#FFFFFF")
cfg.Code.StylePrimitive.Color = new("#C4A7F5")
cfg.Link.Underline = new(true)

r, err := glamour.NewTermRenderer(
    glamour.WithStyles(cfg),
    glamour.WithWordWrap(70),
)
if err != nil {
    fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
}

Read references/styles.md for the full list of StyleConfig blocks and primitives, the exported configs, and the JSON style format.

A Markdown viewer in Bubble Tea

Render to a string, hand it to a viewport, and re-render when the width or the theme changes. The complete viewer example defines model, minWrap, and the message handlers excerpted below. From a repository checkout, run it in a terminal:

cd examples
go run ./charmed-glamour/viewer ../README.md
// examples/charmed-glamour/viewer/main.go
func (m model) Init() tea.Cmd { return tea.RequestBackgroundColor }

func (m model) render() (string, error) {
    style := "light"
    if m.isDark {
        style = "dark"
    }
    r, err := glamour.NewTermRenderer(
        glamour.WithStandardStyle(style),
        glamour.WithWordWrap(max(m.width-2, minWrap)),
    )
    if err != nil {
        return "", err
    }
    return r.Render(m.md)
}

// rerender is called from both message handlers below.
func (m *model) rerender() {
    out, err := m.render()
    if err != nil {
        out = "render error: " + err.Error()
    }
    m.vp.SetContent(out)
}

case tea.BackgroundColorMsg:
    m.isDark = msg.IsDark()
    if m.ready {            // the size may have arrived first
        m.rerender()
    }
case tea.WindowSizeMsg:
    m.width = msg.Width
    // (re)size the viewport, then:
    m.rerender()

WindowSizeMsg and BackgroundColorMsg arrive in either order, so both handlers re-render; otherwise a dark terminal keeps the light rendering until the next resize. minWrap keeps the wrap width positive on tiny terminals. Rendering is not free on large documents; do it on resize, theme, and content change, not on every frame.

Migrating from v1

v1v2
github.com/charmbracelet/glamourcharm.land/glamour/v2 (and /ansi, /styles)
glamour.WithAutoStyle()removed; pass WithStandardStyle (see Mistakes)
glamour.WithColorProfile(p)removed; print with lipgloss.Print
StylePrimitive.Overlinedremoved

Custom ansi.MarginWriter users must call Close(). See the v2.0.1 upgrade guide for migration details. Its background-detection example omits the input and output arguments required by the pinned Lip Gloss version; use the call in the render example.

After migrating, run your application's tests, fix failures, and rerun. Then do the terminal check from step 5 of the workflow. For a viewer, also resize the terminal and confirm that scrolling and background-color updates still work.

Mistakes that cost time

  • Expecting the style to follow the terminal theme. Glamour v2 has no auto style and does not detect the terminal's background or color support, and NewTermRenderer() does not select a named style by itself. Detect light or dark yourself and pass WithStandardStyle explicitly, even where the upgrade guide suggests an implicit dark style.
  • Printing with fmt outside Bubble Tea. Only the lipgloss.Print family downsamples colors to what the terminal can show, so fmt.Print gives wrong colors on a 256 color terminal. Inside Bubble Tea the program's renderer downsamples for you.
  • Rendering a wide table or long code block with WithWordWrap smaller than the content and being surprised by the wrapping. For tables, see WithTableWrap in references/options.md.
  • Re-creating a renderer for every call in a hot path. Build once, reuse.

README.md

tile.json