Format markdown

Resolved 💬 5 comments Opened Aug 12, 2025 by davidgilbertson Closed Aug 21, 2025

The model produces markdown which can make it a bit harder to read with all the bold asterisks, etc.

It's possible to increase readability by using bold and italic Unicode characters. Here's a Python function I use to do this (if I understand correctly you'd do this in Rust).

def markdown_to_unicode(md_text):
    """
    Converts Markdown text to Unicode by transforming Markdown syntax for
     bold and italic text into their corresponding Unicode characters,
     handling lists and headings, and preserving the formatting within code blocks.

    Parameters
    ----------
    md_text : str
        The input Markdown text to be converted.

    Returns
    -------
    str
        The converted text with Unicode characters.
    """
    # Yes you could be clever and generate the below by checking the codepoint offsets, like ('𝑎' - 'a')
    # But you would soon learn that 'ℎ' is special and there's 5 different offsets and its simpler
    # just to list them out in a map.

    # fmt: off
    bold_map = {
        "0": "𝟬", "1": "𝟭", "2": "𝟮", "3": "𝟯", "4": "𝟰", "5": "𝟱", "6": "𝟲", "7": "𝟳", "8": "𝟴", "9": "𝟵",
        "a": "𝗮", "b": "𝗯", "c": "𝗰", "d": "𝗱", "e": "𝗲", "f": "𝗳", "g": "𝗴", "h": "𝗵", "i": "𝗶", "j": "𝗷", "k": "𝗸", "l": "𝗹", "m": "𝗺", "n": "𝗻", "o": "𝗼", "p": "𝗽", "q": "𝗾", "r": "𝗿", "s": "𝘀", "t": "𝘁", "u": "𝘂", "v": "𝘃", "w": "𝘄", "x": "𝘅", "y": "𝘆", "z": "𝘇",
        "A": "𝗔", "B": "𝗕", "C": "𝗖", "D": "𝗗", "E": "𝗘", "F": "𝗙", "G": "𝗚", "H": "𝗛", "I": "𝗜", "J": "𝗝", "K": "𝗞", "L": "𝗟", "M": "𝗠", "N": "𝗡", "O": "𝗢", "P": "𝗣", "Q": "𝗤", "R": "𝗥", "S": "𝗦", "T": "𝗧", "U": "𝗨", "V": "𝗩", "W": "𝗪", "X": "𝗫", "Y": "𝗬", "Z": "𝗭",
    }

    italic_map = {
        "a": "𝑎", "b": "𝑏", "c": "𝑐", "d": "𝑑", "e": "𝑒", "f": "𝑓", "g": "𝑔", "h": "ℎ", "i": "𝑖", "j": "𝑗", "k": "𝑘", "l": "𝑙", "m": "𝑚", "n": "𝑛", "o": "𝑜", "p": "𝑝", "q": "𝑞", "r": "𝑟", "s": "𝑠", "t": "𝑡", "u": "𝑢", "v": "𝑣", "w": "𝑤", "x": "𝑥", "y": "𝑦", "z": "𝑧",
        "A": "𝐴", "B": "𝐵", "C": "𝐶", "D": "𝐷", "E": "𝐸", "F": "𝐹", "G": "𝐺", "H": "𝐻", "I": "𝐼", "J": "𝐽", "K": "𝐾", "L": "𝐿", "M": "𝑀", "N": "𝑁", "O": "𝑂", "P": "𝑃", "Q": "𝑄", "R": "𝑅", "S": "𝑆", "T": "𝑇", "U": "𝑈", "V": "𝑉", "W": "𝑊", "X": "𝑋", "Y": "𝑌", "Z": "𝑍",
    }
    # fmt: on

    def replace(text, char_map):
        return "".join(char_map.get(c, c) for c in text)

    def handle_styles(text):
        text = re.sub(
            r"\*\*(.*?)\*\*",
            lambda m: replace(m.group(1), bold_map),
            text,
        )
        text = re.sub(
            r"\*(.*?)\*",
            lambda m: replace(m.group(1), italic_map),
            text,
        )
        return text

    lines = md_text.split("\n")
    in_code = False
    for i, line in enumerate(lines):
        if line.startswith("```"):
            in_code = not in_code

        if in_code:
            continue

        if line.startswith(("- ", "* ")):
            line = f"• {line[2:]}"

        if re.match("#{1,6} ", line):
            # Treat all headings the same, but keep the '#'
            line = replace(line, bold_map)
        else:
            line = handle_styles(line)

        lines[i] = line

    return "\n".join(lines)

There are downsides to this of course (non-markdown asterisks like in **/*.py cause trouble), so it should probably be an opt-in thing if you deem it worth the effort.

My feelings won't be hurt if you feel there's bigger fish to fry :)

View original on GitHub ↗

This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗