Skip to content

Markdown

markdown renders a Markdown document as a Div of elements, which goes anywhere an element does. It handles GitHub Flavored Markdown, and YAML front matter at the top of a document.

--8 < --"markdown_document.py:example"

A rendered Markdown document

 Peaks                                                      

 The fourteen peaks above 8000 m are all in Asia; see the   
 list.                                                      

 │ Because it's there.                                      

 • Himalayas                                                
   ◦ Everest                                                
   ◦ Kangchenjunga                                          
 • Karakoram                                                
   ◦ K2                                                     

 ┌─────────┬────────────┐                                   
 │ Peak    │ Height (m) │                                   
 ├─────────┼────────────┤                                   
 │ Everest │       8849 │                                   
 ├─────────┼────────────┤                                   
 │ K2      │       8611 │                                   
 └─────────┴────────────┘                                   

  tallest = max(peaks, key=lambda p: p.height)              
  • Links, bare URLs, and images are hyperlinks to their destinations. An image is shown as its alt text, since a terminal can't show the image.
  • Fenced code blocks are highlighted as the language their info string names, or shown as written if it names none, or one Pygments doesn't know.
  • Math, between $s within a line or $$s on lines of their own, is shown as written, since a terminal can't typeset it. A dollar sign starts math only by Pandoc's rule, with no space just inside it and no digit right after the closing one, so $5 and $10 stays text.
  • Raw HTML is shown as written.

GitHub's extensions to CommonMark draw like this:

--8 < --"markdown_document.py:github"

A rendered Markdown document using GitHub's extensions

  peak: K2                                                  

 ☑ Reach base camp                                          
 ☐ Summit, weather permitting[1]                            

 │ Warning                                                  
 │ Bottlenecks form above camp four.                        

 Forecasts are at www.mountain-forecast.com.                

 ────────────────────────────────────────────────────────── 
 [1] Usually in late July.                                  
  • Front matter is shown dimmed and highlighted as YAML. Pass MarkdownStyles(front_matter=display_none) to hide it.
  • A task list item's box takes the place of its bullet, or follows its number.
  • An alert is a block quote with a title, colored by its kind.
  • Footnotes are numbered in the order they're referenced, and their definitions follow the rest of the document.

Rendering is cached by the source and the styles, so a component can render the same document on every render without parsing it again.

Scrolling

A document is as tall as its content, so one longer than the space it's given runs past the bottom of it. Put it in a scroll container, as a browser puts a page in a scrolling viewport, with scroll_on_keys and scroll_on_wheel to scroll it:

--8 < --"markdown_document.py:scrolling"

The top of a rendered document in a bordered box

╭──────────────────────────────────────────────────────────╮
│ Peaks                                                    │
│                                                          │
│ The fourteen peaks above 8000 m are all in Asia; see the │
│ list.                                                    │
│                                                          │
│ │ Because it's there.                                    │
│                                                          │
│ • Himalayas                                              │
│   ◦ Everest                                              │
│   ◦ Kangchenjunga                                        │
╰──────────────────────────────────────────────────────────╯

The document scrolled down a page

╭──────────────────────────────────────────────────────────╮
│   ◦ Everest                                              │
│   ◦ Kangchenjunga                                        │
│ • Karakoram                                              │
│   ◦ K2                                                   │
│                                                          │
│ ┌─────────┬────────────┐                                 │
│ │ Peak    │ Height (m) │                                 │
│ ├─────────┼────────────┤                                 │
│ │ Everest │       8849 │                                 │
│ ├─────────┼────────────┤                                 │
╰──────────────────────────────────────────────────────────╯

The Markdown viewer example puts a tree of a directory's Markdown files beside the one it's showing, each in a pane that scrolls, and the Markdown gallery example shows the source of each kind of block and span beside how it renders.

Styles

MarkdownStyles holds a Style for each kind of block and a CellStyle for each kind of inline span. Pass the fields you want to change, and the rest keep their defaults:

markdown(source, MarkdownStyles(heading_1=text_color("amber", 300), code_theme="monokai", block_gap=0))

API

counterweight.markdown.markdown cached

markdown(
    source: str, styles: MarkdownStyles = MarkdownStyles()
) -> Div

source, a Markdown document, as a Div of its blocks, drawn with styles.

Links, bare URLs, and images are hyperlinks to their destinations. Fenced code blocks are highlighted as the language their info string names, or shown as written if it names none or one Pygments doesn't know. Math and raw HTML are shown as written, too.

counterweight.markdown.MarkdownStyles dataclass

MarkdownStyles(
    *,
    document: Style = Style(),
    block_gap: int = 1,
    paragraph: Style = text_wrap_stable,
    heading_1: Style = text_wrap_stable
    | Style(text_style=BOLD)
    | text_color("sky", 300),
    heading_2: Style = text_wrap_stable
    | Style(text_style=BOLD)
    | text_color("sky", 400),
    heading_3: Style = text_wrap_stable
    | Style(text_style=BOLD)
    | text_color("sky", 500),
    heading_4: Style = text_wrap_stable
    | Style(text_style=BOLD),
    heading_5: Style = text_wrap_stable
    | Style(text_style=BOLD),
    heading_6: Style = text_wrap_stable
    | Style(text_style=BOLD | CellStyle(dim=True)),
    block_quote: Style = border_light
    | border_left
    | border_color("slate", 500)
    | pad_left(1),
    block_quote_text: CellStyle = CellStyle(italic=True)
    | text_style,
    list_marker: Style = text_color("slate", 400),
    bullets: tuple[str, ...] = ("•", "◦", "▪"),
    unchecked_task: str = "☐",
    checked_task: str = "☑",
    checked_task_marker: Style = text_color("emerald", 400),
    alert: Style = border_light | border_left | pad_left(1),
    alert_title: Style = text_wrap_stable
    | Style(text_style=BOLD),
    alert_note: Style = border_color("blue", 400)
    | text_color("blue", 400),
    alert_tip: Style = border_color("green", 400)
    | text_color("green", 400),
    alert_important: Style = border_color("violet", 400)
    | text_color("violet", 400),
    alert_warning: Style = border_color("amber", 400)
    | text_color("amber", 400),
    alert_caution: Style = border_color("red", 400)
    | text_color("red", 400),
    code_block: Style = pad_x(1),
    code_theme: CodeTheme = DEFAULT_CODE_THEME,
    front_matter: Style = Style(
        text_style=CellStyle(dim=True)
    ),
    thematic_break: Style = border_light
    | border_top
    | border_color("slate", 600),
    math_block: Style = text_wrap_stable
    | text_justify_center
    | text_color("violet", 300),
    math_label: Style = text_color("slate", 400),
    html_block: Style = Style(
        text_style=CellStyle(dim=True)
    ),
    table: Style = align_self_start,
    table_cell: Style = border_light
    | border
    | border_color("slate", 600),
    table_cell_text: Style = pad_x(1) | text_wrap_stable,
    table_header: Style = Style(text_style=BOLD),
    emphasis: CellStyle = CellStyle(italic=True),
    strong: CellStyle = BOLD,
    strikethrough: CellStyle = CellStyle(
        strikethrough=True
    ),
    code_inline: CellStyle = text_style | text_style,
    math_inline: CellStyle = text_style,
    link: CellStyle = CellStyle(underline=True)
    | text_style,
    image: CellStyle = CellStyle(italic=True) | text_style,
    footnote_ref: CellStyle = text_style,
    footnotes: Style = border_light
    | border_top
    | border_color("slate", 600)
)

How markdown draws each kind of Markdown block and inline span.

Each block becomes an element styled by its Style here, merged over the layout markdown gives it. Each inline span's CellStyle is merged over the text around it, so ***both*** is bold and italic. Pass the fields you want to change: MarkdownStyles(paragraph=text_wrap_pretty).

document class-attribute instance-attribute

document: Style = Style()

The Div holding the document's blocks.

block_gap class-attribute instance-attribute

block_gap: int = 1

Rows between blocks: in the document, a block quote, and a loose list and its items.

paragraph class-attribute instance-attribute

paragraph: Style = text_wrap_stable

heading_1 class-attribute instance-attribute

heading_1: Style = (
    text_wrap_stable
    | Style(text_style=BOLD)
    | text_color("sky", 300)
)

heading_2 class-attribute instance-attribute

heading_2: Style = (
    text_wrap_stable
    | Style(text_style=BOLD)
    | text_color("sky", 400)
)

heading_3 class-attribute instance-attribute

heading_3: Style = (
    text_wrap_stable
    | Style(text_style=BOLD)
    | text_color("sky", 500)
)

heading_4 class-attribute instance-attribute

heading_4: Style = text_wrap_stable | Style(text_style=BOLD)

heading_5 class-attribute instance-attribute

heading_5: Style = text_wrap_stable | Style(text_style=BOLD)

heading_6 class-attribute instance-attribute

heading_6: Style = text_wrap_stable | Style(
    text_style=BOLD | CellStyle(dim=True)
)

block_quote class-attribute instance-attribute

block_quote: Style = (
    border_light
    | border_left
    | border_color("slate", 500)
    | pad_left(1)
)

block_quote_text class-attribute instance-attribute

block_quote_text: CellStyle = (
    CellStyle(italic=True)
    | text_color("slate", 400).text_style
)

Merged over the text of everything inside a block quote.

list_marker class-attribute instance-attribute

list_marker: Style = text_color('slate', 400)

bullets class-attribute instance-attribute

bullets: tuple[str, ...] = ('•', '◦', '▪')

The bullet for each depth of nested bullet lists, starting again from the first past the last.

unchecked_task class-attribute instance-attribute

unchecked_task: str = '☐'

The box of a task list item that isn't done, which takes the place of its bullet, or follows its number.

checked_task class-attribute instance-attribute

checked_task: str = '☑'

The box of a task list item that is done.

checked_task_marker class-attribute instance-attribute

checked_task_marker: Style = text_color('emerald', 400)

Merged over list_marker for the box of a task list item that is done.

alert class-attribute instance-attribute

alert: Style = border_light | border_left | pad_left(1)

A GitHub alert (> [!NOTE]), which is a block quote with a title.

alert_title class-attribute instance-attribute

alert_title: Style = text_wrap_stable | Style(
    text_style=BOLD
)

alert_note class-attribute instance-attribute

alert_note: Style = border_color("blue", 400) | text_color(
    "blue", 400
)

Merged over alert for a NOTE alert, and over alert_title for its title, so its border color colors the alert's bar and its text color the title.

alert_tip class-attribute instance-attribute

alert_tip: Style = border_color("green", 400) | text_color(
    "green", 400
)

Merged over alert and alert_title for a TIP alert, like alert_note.

alert_important class-attribute instance-attribute

alert_important: Style = border_color(
    "violet", 400
) | text_color("violet", 400)

Merged over alert and alert_title for an IMPORTANT alert, like alert_note.

alert_warning class-attribute instance-attribute

alert_warning: Style = border_color(
    "amber", 400
) | text_color("amber", 400)

Merged over alert and alert_title for a WARNING alert, like alert_note.

alert_caution class-attribute instance-attribute

alert_caution: Style = border_color(
    "red", 400
) | text_color("red", 400)

Merged over alert and alert_title for a CAUTION alert, like alert_note.

code_block class-attribute instance-attribute

code_block: Style = pad_x(1)

Merged over the code theme's background, from code_style.

code_theme class-attribute instance-attribute

The theme fenced code blocks are highlighted with, when their info string names a language Pygments knows.

front_matter class-attribute instance-attribute

front_matter: Style = Style(text_style=CellStyle(dim=True))

Merged over code_block for the document's YAML front matter, which is highlighted as YAML. Pass display_none to hide it.

thematic_break class-attribute instance-attribute

thematic_break: Style = (
    border_light | border_top | border_color("slate", 600)
)

math_block class-attribute instance-attribute

math_block: Style = (
    text_wrap_stable
    | text_justify_center
    | text_color("violet", 300)
)

Display math, between $$ lines, which is shown as written, since a terminal can't typeset it.

math_label class-attribute instance-attribute

math_label: Style = text_color('slate', 400)

The label of display math written $$...$$ (label), shown as (label) to its right.

html_block class-attribute instance-attribute

html_block: Style = Style(text_style=CellStyle(dim=True))

Raw HTML blocks, which are shown as written.

table class-attribute instance-attribute

table: Style = align_self_start

table_cell class-attribute instance-attribute

table_cell: Style = (
    border_light | border | border_color("slate", 600)
)

The Div around each cell's text, which draws its border.

table_cell_text class-attribute instance-attribute

table_cell_text: Style = pad_x(1) | text_wrap_stable

table_header class-attribute instance-attribute

table_header: Style = Style(text_style=BOLD)

Merged over table_cell_text for the header row's cells.

emphasis class-attribute instance-attribute

emphasis: CellStyle = CellStyle(italic=True)

strong class-attribute instance-attribute

strong: CellStyle = BOLD

strikethrough class-attribute instance-attribute

strikethrough: CellStyle = CellStyle(strikethrough=True)

code_inline class-attribute instance-attribute

code_inline: CellStyle = (
    text_color("rose", 300).text_style
    | text_bg("slate", 800).text_style
)

math_inline class-attribute instance-attribute

math_inline: CellStyle = text_color(
    "violet", 300
).text_style

Math within a line, between $s or $$s, which is shown as written.

link: CellStyle = (
    CellStyle(underline=True)
    | text_color("sky", 300).text_style
)

Merged under the CellStyle(link=...) that makes a link's text a hyperlink to its destination.

image class-attribute instance-attribute

image: CellStyle = (
    CellStyle(italic=True)
    | text_color("sky", 300).text_style
)

An image's alt text, which links to the image, since a terminal can't show it.

footnote_ref class-attribute instance-attribute

footnote_ref: CellStyle = text_color('sky', 300).text_style

A footnote's number, [1], where it's referenced and beside its definition.

footnotes class-attribute instance-attribute

footnotes: Style = (
    border_light | border_top | border_color("slate", 600)
)

The footnotes' definitions, which follow the rest of the document, numbered in the order they're referenced.