Skip to main content

src/spruce@markdown.erl

-module(spruce@markdown).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/spruce/markdown.gleam").
-export([dark_theme/0, light_theme/0, adaptive_theme/0, default_options/0, with_theme/2, with_width/2, render_with/3, render/2, print/2]).
-export_type([theme/0, options/0, alert/0, alert_kind/0, fence/0]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

?MODULEDOC(
    " Markdown to ANSI terminal rendering.\n"
    "\n"
    " Rendering is driven by the [`mork`](https://codeberg.org/krig/mork)\n"
    " parser (canonical home: <https://git.liten.app/krig/mork>) and walks its\n"
    " document AST directly rather than going through `mork`'s HTML output.\n"
    "\n"
    " ## GFM support\n"
    "\n"
    " The following GitHub Flavored Markdown extensions are supported:\n"
    "\n"
    " - Tables (rendered via `spruce/table`)\n"
    " - Task list items (`- [x]` / `- [ ]`)\n"
    " - Strikethrough (`~~text~~`)\n"
    " - Extended autolinks for bare URLs and `www.` links\n"
    "\n"
    " In addition, GitHub-style alerts (`> [!NOTE]`, `[!TIP]`, `[!IMPORTANT]`,\n"
    " `[!WARNING]`, `[!CAUTION]`) and Astro/Starlight `:::type[Title]` container\n"
    " directives are rendered as colored callouts.\n"
    "\n"
    " ## Known limitations\n"
    "\n"
    " These are GitHub/Markdown features that are *not* rendered. Most stem from\n"
    " upstream `mork` (tracked in its\n"
    " [TODO.md](https://git.liten.app/krig/mork/src/branch/main/TODO.md)); a few\n"
    " are deliberate choices here.\n"
    "\n"
    " - **Emoji shortcodes** (`:rocket:`) are not expanded. `mork` only expands\n"
    "   them in its HTML output path, not in the document AST this module\n"
    "   renders, so enabling `mork.emojis` has no effect here.\n"
    " - **Email autolinks** (bare `me@example.com`) are not linked; extended\n"
    "   email autolinking is unimplemented upstream (`mork` TODO: \"autolink\n"
    "   (email)\").\n"
    " - **Footnotes**: a `[^1]` reference renders as literal `[^1]` and the\n"
    "   definition body is dropped. Footnote bodies are not yet implemented\n"
    "   upstream, and inline footnotes (`^[...]`) are unsupported.\n"
    " - **GFM table column alignment** (`:--`, `:-:`, `--:`) is parsed by `mork`\n"
    "   but ignored here: every cell is left-aligned, because `spruce/table`\n"
    "   does not expose per-column alignment.\n"
    " - **Heading ID attributes** (`## Title {#id}`) are stripped from the\n"
    "   rendered text (via `mork.heading_ids`), since a terminal has no anchors\n"
    "   to link to. The id itself is parsed but not rendered.\n"
    " - **Raw HTML is not sanitized.** Inline and block HTML is passed through\n"
    "   (rendered dimmed), not escaped or stripped. `mork` does not implement\n"
    "   GFM's tagfilter. This is harmless in a terminal, but do not rely on this\n"
    "   module to neutralize untrusted HTML.\n"
    "\n"
).

-opaque theme() :: {theme,
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@style:style(),
        spruce@highlight:theme(),
        spruce@style:color(),
        spruce@style:color(),
        spruce@style:style(),
        spruce@style:style()}.

-opaque options() :: {options, theme(), gleam@option:option(integer())}.

-type alert() :: {alert,
        alert_kind(),
        gleam@option:option(list(mork@document:inline())),
        list(mork@document:block())}.

-type alert_kind() :: alert_note |
    alert_tip |
    alert_important |
    alert_warning |
    alert_caution.

-type fence() :: {fence, binary(), integer()}.

-file("src/spruce/markdown.gleam", 100).
?DOC(" Build a theme tuned for dark terminal backgrounds.\n").
-spec dark_theme() -> theme().
dark_theme() ->
    {theme,
        begin
            _pipe = spruce@style:new(),
            _pipe@1 = spruce@style:bold(_pipe),
            spruce@style:fg(_pipe@1, {hex, 16#7dd3fc})
        end,
        begin
            _pipe@2 = spruce@style:new(),
            _pipe@3 = spruce@style:bold(_pipe@2),
            spruce@style:fg(_pipe@3, {hex, 16#93c5fd})
        end,
        begin
            _pipe@4 = spruce@style:new(),
            _pipe@5 = spruce@style:bold(_pipe@4),
            spruce@style:fg(_pipe@5, {hex, 16#c4b5fd})
        end,
        begin
            _pipe@6 = spruce@style:new(),
            _pipe@7 = spruce@style:bold(_pipe@6),
            spruce@style:fg(_pipe@7, {hex, 16#f0abfc})
        end,
        begin
            _pipe@8 = spruce@style:new(),
            _pipe@9 = spruce@style:bold(_pipe@8),
            spruce@style:fg(_pipe@9, {hex, 16#f9a8d4})
        end,
        begin
            _pipe@10 = spruce@style:new(),
            _pipe@11 = spruce@style:bold(_pipe@10),
            spruce@style:fg(_pipe@11, {hex, 16#fda4af})
        end,
        begin
            _pipe@12 = spruce@style:new(),
            spruce@style:italic(_pipe@12)
        end,
        begin
            _pipe@13 = spruce@style:new(),
            spruce@style:bold(_pipe@13)
        end,
        begin
            _pipe@14 = spruce@style:new(),
            spruce@style:strikethrough(_pipe@14)
        end,
        begin
            _pipe@15 = spruce@style:new(),
            spruce@style:reverse(_pipe@15)
        end,
        begin
            _pipe@16 = spruce@style:new(),
            _pipe@17 = spruce@style:fg(_pipe@16, {hex, 16#fbbf24}),
            spruce@style:dim(_pipe@17)
        end,
        begin
            _pipe@18 = spruce@style:new(),
            _pipe@19 = spruce@style:underline(_pipe@18),
            spruce@style:fg(_pipe@19, {hex, 16#60a5fa})
        end,
        begin
            _pipe@20 = spruce@style:new(),
            spruce@style:dim(_pipe@20)
        end,
        begin
            _pipe@21 = spruce@style:new(),
            spruce@style:dim(_pipe@21)
        end,
        spruce@highlight:dark_theme(),
        {hex, 16#64748b},
        {hex, 16#94a3b8},
        begin
            _pipe@22 = spruce@style:new(),
            spruce@style:dim(_pipe@22)
        end,
        begin
            _pipe@23 = spruce@style:new(),
            spruce@style:bold(_pipe@23)
        end}.

-file("src/spruce/markdown.gleam", 125).
?DOC(" Build a theme tuned for light terminal backgrounds.\n").
-spec light_theme() -> theme().
light_theme() ->
    {theme,
        begin
            _pipe = spruce@style:new(),
            _pipe@1 = spruce@style:bold(_pipe),
            spruce@style:fg(_pipe@1, {hex, 16#0369a1})
        end,
        begin
            _pipe@2 = spruce@style:new(),
            _pipe@3 = spruce@style:bold(_pipe@2),
            spruce@style:fg(_pipe@3, {hex, 16#1d4ed8})
        end,
        begin
            _pipe@4 = spruce@style:new(),
            _pipe@5 = spruce@style:bold(_pipe@4),
            spruce@style:fg(_pipe@5, {hex, 16#6d28d9})
        end,
        begin
            _pipe@6 = spruce@style:new(),
            _pipe@7 = spruce@style:bold(_pipe@6),
            spruce@style:fg(_pipe@7, {hex, 16#a21caf})
        end,
        begin
            _pipe@8 = spruce@style:new(),
            _pipe@9 = spruce@style:bold(_pipe@8),
            spruce@style:fg(_pipe@9, {hex, 16#be185d})
        end,
        begin
            _pipe@10 = spruce@style:new(),
            _pipe@11 = spruce@style:bold(_pipe@10),
            spruce@style:fg(_pipe@11, {hex, 16#be123c})
        end,
        begin
            _pipe@12 = spruce@style:new(),
            spruce@style:italic(_pipe@12)
        end,
        begin
            _pipe@13 = spruce@style:new(),
            spruce@style:bold(_pipe@13)
        end,
        begin
            _pipe@14 = spruce@style:new(),
            spruce@style:strikethrough(_pipe@14)
        end,
        begin
            _pipe@15 = spruce@style:new(),
            spruce@style:reverse(_pipe@15)
        end,
        begin
            _pipe@16 = spruce@style:new(),
            _pipe@17 = spruce@style:fg(_pipe@16, {hex, 16#92400e}),
            spruce@style:dim(_pipe@17)
        end,
        begin
            _pipe@18 = spruce@style:new(),
            _pipe@19 = spruce@style:underline(_pipe@18),
            spruce@style:fg(_pipe@19, {hex, 16#2563eb})
        end,
        begin
            _pipe@20 = spruce@style:new(),
            spruce@style:dim(_pipe@20)
        end,
        begin
            _pipe@21 = spruce@style:new(),
            spruce@style:dim(_pipe@21)
        end,
        spruce@highlight:light_theme(),
        {hex, 16#475569},
        {hex, 16#64748b},
        begin
            _pipe@22 = spruce@style:new(),
            spruce@style:dim(_pipe@22)
        end,
        begin
            _pipe@23 = spruce@style:new(),
            spruce@style:bold(_pipe@23)
        end}.

-file("src/spruce/markdown.gleam", 153).
?DOC(
    " A theme whose colors adapt to the terminal background (light vs dark),\n"
    " resolved per render from `spruce.background`. This is the default theme used\n"
    " by `render` and `default_options`. On `Unknown` backgrounds it renders as\n"
    " dark.\n"
).
-spec adaptive_theme() -> theme().
adaptive_theme() ->
    Adapt = fun(Light, Dark) ->
        spruce@style:adaptive({hex, Light}, {hex, Dark})
    end,
    {theme,
        begin
            _pipe = spruce@style:new(),
            _pipe@1 = spruce@style:bold(_pipe),
            spruce@style:fg(_pipe@1, Adapt(16#0369a1, 16#7dd3fc))
        end,
        begin
            _pipe@2 = spruce@style:new(),
            _pipe@3 = spruce@style:bold(_pipe@2),
            spruce@style:fg(_pipe@3, Adapt(16#1d4ed8, 16#93c5fd))
        end,
        begin
            _pipe@4 = spruce@style:new(),
            _pipe@5 = spruce@style:bold(_pipe@4),
            spruce@style:fg(_pipe@5, Adapt(16#6d28d9, 16#c4b5fd))
        end,
        begin
            _pipe@6 = spruce@style:new(),
            _pipe@7 = spruce@style:bold(_pipe@6),
            spruce@style:fg(_pipe@7, Adapt(16#a21caf, 16#f0abfc))
        end,
        begin
            _pipe@8 = spruce@style:new(),
            _pipe@9 = spruce@style:bold(_pipe@8),
            spruce@style:fg(_pipe@9, Adapt(16#be185d, 16#f9a8d4))
        end,
        begin
            _pipe@10 = spruce@style:new(),
            _pipe@11 = spruce@style:bold(_pipe@10),
            spruce@style:fg(_pipe@11, Adapt(16#be123c, 16#fda4af))
        end,
        begin
            _pipe@12 = spruce@style:new(),
            spruce@style:italic(_pipe@12)
        end,
        begin
            _pipe@13 = spruce@style:new(),
            spruce@style:bold(_pipe@13)
        end,
        begin
            _pipe@14 = spruce@style:new(),
            spruce@style:strikethrough(_pipe@14)
        end,
        begin
            _pipe@15 = spruce@style:new(),
            spruce@style:reverse(_pipe@15)
        end,
        begin
            _pipe@16 = spruce@style:new(),
            _pipe@17 = spruce@style:fg(_pipe@16, Adapt(16#92400e, 16#fbbf24)),
            spruce@style:dim(_pipe@17)
        end,
        begin
            _pipe@18 = spruce@style:new(),
            _pipe@19 = spruce@style:underline(_pipe@18),
            spruce@style:fg(_pipe@19, Adapt(16#2563eb, 16#60a5fa))
        end,
        begin
            _pipe@20 = spruce@style:new(),
            spruce@style:dim(_pipe@20)
        end,
        begin
            _pipe@21 = spruce@style:new(),
            spruce@style:dim(_pipe@21)
        end,
        spruce@highlight:adaptive_theme(),
        Adapt(16#475569, 16#64748b),
        Adapt(16#64748b, 16#94a3b8),
        begin
            _pipe@22 = spruce@style:new(),
            spruce@style:dim(_pipe@22)
        end,
        begin
            _pipe@23 = spruce@style:new(),
            spruce@style:bold(_pipe@23)
        end}.

-file("src/spruce/markdown.gleam", 181).
?DOC(" Default options: the adaptive theme and no width limit.\n").
-spec default_options() -> options().
default_options() ->
    {options, adaptive_theme(), none}.

-file("src/spruce/markdown.gleam", 186).
?DOC(" Use a specific theme when rendering.\n").
-spec with_theme(options(), theme()) -> options().
with_theme(Options, Theme) ->
    {options, Theme, erlang:element(3, Options)}.

-file("src/spruce/markdown.gleam", 191).
?DOC(" Wrap rendered output to a maximum visual width. Negative values clamp to 0.\n").
-spec with_width(options(), integer()) -> options().
with_width(Options, Width) ->
    {options, erlang:element(2, Options), {some, gleam@int:max(0, Width)}}.

-file("src/spruce/markdown.gleam", 1032).
-spec remove_empty(list(binary())) -> list(binary()).
remove_empty(Lines) ->
    case Lines of
        [] ->
            [];

        [<<""/utf8>> | Rest] ->
            remove_empty(Rest);

        [Line | Rest@1] ->
            [Line | remove_empty(Rest@1)]
    end.

-file("src/spruce/markdown.gleam", 846).
-spec render_rule(spruce:spruce(), options()) -> binary().
render_rule(Sp, Options) ->
    Width@1 = case erlang:element(3, Options) of
        {some, Width} when Width > 0 ->
            Width;

        _ ->
            40
    end,
    <<(spruce@internal@layout:indent_prefix(Sp))/binary,
        (spruce@style:render(
            Sp,
            erlang:element(19, erlang:element(2, Options)),
            gleam@string:repeat(<<"─"/utf8>>, Width@1)
        ))/binary>>.

-file("src/spruce/markdown.gleam", 1003).
-spec destination_string(mork@document:destination()) -> binary().
destination_string(Destination) ->
    case Destination of
        {absolute, Uri} ->
            Uri;

        {relative, Uri@1} ->
            Uri@1;

        {anchor, Id} ->
            <<"#"/utf8, Id/binary>>
    end.

-file("src/spruce/markdown.gleam", 951).
-spec render_link(spruce:spruce(), binary(), binary(), theme()) -> binary().
render_link(Sp, Label, Target, Theme) ->
    Visible = spruce@style:render(Sp, erlang:element(13, Theme), Label),
    case (Target =:= <<""/utf8>>) orelse (Target =:= Label) of
        true ->
            Visible;

        false ->
            <<Visible/binary,
                (spruce@style:render(
                    Sp,
                    erlang:element(14, Theme),
                    <<<<" ("/utf8, Target/binary>>/binary, ")"/utf8>>
                ))/binary>>
    end.

-file("src/spruce/markdown.gleam", 966).
-spec render_image(
    spruce:spruce(),
    list(mork@document:inline()),
    binary(),
    options()
) -> binary().
render_image(Sp, Text, Target, Options) ->
    Label = render_inlines(Sp, Text, Options),
    case Label of
        <<""/utf8>> ->
            Target;

        _ ->
            Label
    end.

-file("src/spruce/markdown.gleam", 885).
-spec render_inline(spruce:spruce(), mork@document:inline(), options()) -> binary().
render_inline(Sp, Inline, Options) ->
    case Inline of
        {autolink, Uri, Text} ->
            Label = case Text of
                {some, Text@1} ->
                    Text@1;

                none ->
                    Uri
            end,
            render_link(Sp, Label, Uri, erlang:element(2, Options));

        {code_span, Text@2} ->
            spruce@style:render(
                Sp,
                erlang:element(12, erlang:element(2, Options)),
                <<<<"`"/utf8, Text@2/binary>>/binary, "`"/utf8>>
            );

        {email_autolink, Mail} ->
            render_link(
                Sp,
                Mail,
                <<"mailto:"/utf8, Mail/binary>>,
                erlang:element(2, Options)
            );

        {emphasis, Children} ->
            spruce@style:render(
                Sp,
                erlang:element(8, erlang:element(2, Options)),
                render_inlines(Sp, Children, Options)
            );

        {footnote, Num, _} ->
            <<<<"[^"/utf8, (erlang:integer_to_binary(Num))/binary>>/binary,
                "]"/utf8>>;

        {full_image, Text@3, Data} ->
            render_image(
                Sp,
                Text@3,
                destination_string(erlang:element(2, Data)),
                Options
            );

        {full_link, Text@4, Data@1} ->
            render_link(
                Sp,
                render_inlines(Sp, Text@4, Options),
                destination_string(erlang:element(2, Data@1)),
                erlang:element(2, Options)
            );

        hard_break ->
            <<"\n"/utf8>>;

        {highlight, Children@1} ->
            spruce@style:render(
                Sp,
                erlang:element(11, erlang:element(2, Options)),
                render_inlines(Sp, Children@1, Options)
            );

        {inline_footnote, Num@1, _} ->
            <<<<"[^"/utf8, (erlang:integer_to_binary(Num@1))/binary>>/binary,
                "]"/utf8>>;

        {inline_html, Tag, _, Children@2} ->
            case Children@2 of
                [] ->
                    spruce@style:render(
                        Sp,
                        erlang:element(15, erlang:element(2, Options)),
                        <<<<"<"/utf8, Tag/binary>>/binary, ">"/utf8>>
                    );

                _ ->
                    render_inlines(Sp, Children@2, Options)
            end;

        {raw_html, Raw} ->
            spruce@style:render(
                Sp,
                erlang:element(15, erlang:element(2, Options)),
                Raw
            );

        {ref_image, Text@5, Label@1} ->
            render_image(Sp, Text@5, Label@1, Options);

        {ref_link, Text@6, _} ->
            render_inlines(Sp, Text@6, Options);

        soft_break ->
            <<" "/utf8>>;

        {strikethrough, Children@3} ->
            spruce@style:render(
                Sp,
                erlang:element(10, erlang:element(2, Options)),
                render_inlines(Sp, Children@3, Options)
            );

        {strong, Children@4} ->
            spruce@style:render(
                Sp,
                erlang:element(9, erlang:element(2, Options)),
                render_inlines(Sp, Children@4, Options)
            );

        {text, Text@7} ->
            Text@7;

        {checkbox, true} ->
            <<"[x]"/utf8>>;

        {checkbox, false} ->
            <<"[ ]"/utf8>>;

        {delim, Delimiter, Len, _, _} ->
            gleam@string:repeat(Delimiter, Len)
    end.

-file("src/spruce/markdown.gleam", 871).
-spec render_inline_list(
    list(mork@document:inline()),
    spruce:spruce(),
    options()
) -> list(binary()).
render_inline_list(Inlines, Sp, Options) ->
    case Inlines of
        [] ->
            [];

        [First | Rest] ->
            [render_inline(Sp, First, Options) |
                render_inline_list(Rest, Sp, Options)]
    end.

-file("src/spruce/markdown.gleam", 861).
-spec render_inlines(spruce:spruce(), list(mork@document:inline()), options()) -> binary().
render_inlines(Sp, Inlines, Options) ->
    _pipe = Inlines,
    _pipe@1 = render_inline_list(_pipe, Sp, Options),
    gleam@string:join(_pipe@1, <<""/utf8>>).

-file("src/spruce/markdown.gleam", 980).
-spec fallback_inline(
    spruce:spruce(),
    binary(),
    list(mork@document:inline()),
    options()
) -> binary().
fallback_inline(Sp, Raw, Inlines, Options) ->
    case render_inlines(Sp, Inlines, Options) of
        <<""/utf8>> ->
            Raw;

        Text ->
            Text
    end.

-file("src/spruce/markdown.gleam", 832).
-spec render_table_cells(spruce:spruce(), list(mork@document:cell()), options()) -> list(binary()).
render_table_cells(Sp, Cells, Options) ->
    case Cells of
        [] ->
            [];

        [{cell, Raw, Inlines} | Rest] ->
            [begin
                    _pipe = fallback_inline(Sp, Raw, Inlines, Options),
                    gleam@string:trim(_pipe)
                end |
                render_table_cells(Sp, Rest, Options)]
    end.

-file("src/spruce/markdown.gleam", 818).
-spec render_table_rows(
    spruce:spruce(),
    list(list(mork@document:cell())),
    options()
) -> list(list(binary())).
render_table_rows(Sp, Rows, Options) ->
    case Rows of
        [] ->
            [];

        [Row | Rest] ->
            [render_table_cells(Sp, Row, Options) |
                render_table_rows(Sp, Rest, Options)]
    end.

-file("src/spruce/markdown.gleam", 804).
-spec render_table_headers(
    spruce:spruce(),
    list(mork@document:t_head()),
    options()
) -> list(binary()).
render_table_headers(Sp, Headers, Options) ->
    case Headers of
        [] ->
            [];

        [{t_head, _, Raw, Inlines} | Rest] ->
            [begin
                    _pipe = fallback_inline(Sp, Raw, Inlines, Options),
                    gleam@string:trim(_pipe)
                end |
                render_table_headers(Sp, Rest, Options)]
    end.

-file("src/spruce/markdown.gleam", 781).
-spec render_table(
    spruce:spruce(),
    list(mork@document:t_head()),
    list(list(mork@document:cell())),
    options()
) -> binary().
render_table(Sp, Headers, Rows, Options) ->
    Table_ = begin
        _pipe = spruce@table:new(),
        _pipe@1 = spruce@table:headers(
            _pipe,
            render_table_headers(Sp, Headers, Options)
        ),
        _pipe@2 = spruce@table:rows(
            _pipe@1,
            render_table_rows(Sp, Rows, Options)
        ),
        spruce@table:style_fn(_pipe@2, fun(Row, _) -> case Row of
                    -1 ->
                        erlang:element(20, erlang:element(2, Options));

                    _ ->
                        spruce@style:new()
                end end)
    end,
    case erlang:element(3, Options) of
        {some, Width} when Width > 0 ->
            spruce@table:render(Sp, spruce@table:width(Table_, Width));

        _ ->
            spruce@table:render(Sp, Table_)
    end.

-file("src/spruce/markdown.gleam", 1025).
-spec prefix_lines(binary(), binary()) -> binary().
prefix_lines(Text, Prefix) ->
    _pipe = Text,
    _pipe@1 = gleam@string:split(_pipe, <<"\n"/utf8>>),
    _pipe@2 = gleam@list:map(
        _pipe@1,
        fun(Line) -> <<Prefix/binary, Line/binary>> end
    ),
    gleam@string:join(_pipe@2, <<"\n"/utf8>>).

-file("src/spruce/markdown.gleam", 1018).
-spec wrap(binary(), gleam@option:option(integer())) -> binary().
wrap(Text, Width) ->
    case Width of
        {some, Width@1} when Width@1 > 0 ->
            spruce@align:wrap(Text, Width@1);

        _ ->
            Text
    end.

-file("src/spruce/markdown.gleam", 279).
-spec render_paragraph(spruce:spruce(), list(mork@document:inline()), options()) -> binary().
render_paragraph(Sp, Inlines, Options) ->
    _pipe = render_inlines(Sp, Inlines, Options),
    _pipe@1 = wrap(_pipe, erlang:element(3, Options)),
    prefix_lines(_pipe@1, spruce@internal@layout:indent_prefix(Sp)).

-file("src/spruce/markdown.gleam", 856).
-spec render_html_block(spruce:spruce(), binary(), theme()) -> binary().
render_html_block(Sp, Raw, Theme) ->
    _pipe = spruce@style:render(Sp, erlang:element(15, Theme), Raw),
    prefix_lines(_pipe, spruce@internal@layout:indent_prefix(Sp)).

-file("src/spruce/markdown.gleam", 992).
-spec heading_style(theme(), integer()) -> spruce@style:style().
heading_style(Theme, Level) ->
    case Level of
        1 ->
            erlang:element(2, Theme);

        2 ->
            erlang:element(3, Theme);

        3 ->
            erlang:element(4, Theme);

        4 ->
            erlang:element(5, Theme);

        5 ->
            erlang:element(6, Theme);

        _ ->
            erlang:element(7, Theme)
    end.

-file("src/spruce/markdown.gleam", 261).
-spec render_heading(
    spruce:spruce(),
    integer(),
    binary(),
    list(mork@document:inline()),
    options()
) -> binary().
render_heading(Sp, Level, Raw, Inlines, Options) ->
    Text@1 = case render_inlines(Sp, Inlines, Options) of
        <<""/utf8>> ->
            Raw;

        Text ->
            Text
    end,
    Marker = gleam@string:repeat(<<"#"/utf8>>, gleam@int:clamp(Level, 1, 6)),
    Line = <<<<Marker/binary, " "/utf8>>/binary,
        (gleam@string:trim(Text@1))/binary>>,
    <<(spruce@internal@layout:indent_prefix(Sp))/binary,
        (spruce@style:render(
            Sp,
            heading_style(erlang:element(2, Options), Level),
            Line
        ))/binary>>.

-file("src/spruce/markdown.gleam", 1011).
-spec option_string(gleam@option:option(binary())) -> binary().
option_string(Value) ->
    case Value of
        {some, Value@1} ->
            Value@1;

        none ->
            <<""/utf8>>
    end.

-file("src/spruce/markdown.gleam", 289).
-spec render_code(
    spruce:spruce(),
    gleam@option:option(binary()),
    binary(),
    options()
) -> binary().
render_code(Sp, Lang, Text, Options) ->
    Title = option_string(Lang),
    Highlighted = spruce@highlight:highlight_named_with(
        Sp,
        Text,
        Title,
        erlang:element(16, erlang:element(2, Options))
    ),
    Options_ = begin
        _pipe = spruce@box:options(
            Title,
            erlang:element(17, erlang:element(2, Options))
        ),
        spruce@box:padding(_pipe, 1, 0, 0, 0)
    end,
    spruce@box:render(Sp, Highlighted, Options_).

-file("src/spruce/markdown.gleam", 469).
-spec alert_properties(alert_kind()) -> {spruce@style:color(),
    binary(),
    binary()}.
alert_properties(Kind) ->
    Adapt = fun(Light, Dark) ->
        spruce@style:adaptive({hex, Light}, {hex, Dark})
    end,
    case Kind of
        alert_note ->
            {Adapt(16#1d4ed8, 16#60a5fa), <<"ℹ"/utf8>>, <<"Note"/utf8>>};

        alert_tip ->
            {Adapt(16#15803d, 16#4ade80), <<"✔"/utf8>>, <<"Tip"/utf8>>};

        alert_important ->
            {Adapt(16#7e22ce, 16#c084fc), <<"◉"/utf8>>, <<"Important"/utf8>>};

        alert_warning ->
            {Adapt(16#b45309, 16#fbbf24), <<"⚠"/utf8>>, <<"Warning"/utf8>>};

        alert_caution ->
            {Adapt(16#b91c1c, 16#f87171), <<"✖"/utf8>>, <<"Caution"/utf8>>}
    end.

-file("src/spruce/markdown.gleam", 417).
-spec take_until_break(
    list(mork@document:inline()),
    list(mork@document:inline())
) -> {list(mork@document:inline()), list(mork@document:inline())}.
take_until_break(Inlines, Acc) ->
    case Inlines of
        [] ->
            {lists:reverse(Acc), []};

        [soft_break | Rest] ->
            {lists:reverse(Acc), Rest};

        [hard_break | Rest@1] ->
            {lists:reverse(Acc), Rest@1};

        [First | Rest@2] ->
            take_until_break(Rest@2, [First | Acc])
    end.

-file("src/spruce/markdown.gleam", 406).
?DOC(
    " Split inlines that follow `[!TYPE]` into an optional same-line title (the\n"
    " inlines before the first line break) and the remaining body inlines.\n"
).
-spec split_alert_first_line(list(mork@document:inline())) -> {gleam@option:option(list(mork@document:inline())),
    list(mork@document:inline())}.
split_alert_first_line(Inlines) ->
    {Before, After} = take_until_break(Inlines, []),
    Title = case Before of
        [] ->
            none;

        _ ->
            {some, Before}
    end,
    {Title, After}.

-file("src/spruce/markdown.gleam", 391).
-spec alert_kind_from_name(binary()) -> {ok, alert_kind()} | {error, nil}.
alert_kind_from_name(Name) ->
    case string:lowercase(gleam@string:trim(Name)) of
        <<"note"/utf8>> ->
            {ok, alert_note};

        <<"info"/utf8>> ->
            {ok, alert_note};

        <<"tip"/utf8>> ->
            {ok, alert_tip};

        <<"important"/utf8>> ->
            {ok, alert_important};

        <<"warning"/utf8>> ->
            {ok, alert_warning};

        <<"caution"/utf8>> ->
            {ok, alert_caution};

        <<"danger"/utf8>> ->
            {ok, alert_caution};

        _ ->
            {error, nil}
    end.

-file("src/spruce/markdown.gleam", 386).
-spec alert_kind_from_tag(binary()) -> {ok, alert_kind()} | {error, nil}.
alert_kind_from_tag(Tag) ->
    gleam@bool:guard(
        not gleam_stdlib:string_starts_with(Tag, <<"!"/utf8>>),
        {error, nil},
        fun() -> alert_kind_from_name(gleam@string:drop_start(Tag, 1)) end
    ).

-file("src/spruce/markdown.gleam", 363).
?DOC(
    " Detect a `> [!TYPE] optional title` alert at the head of a block quote and\n"
    " split out its (optional) custom title and remaining body blocks.\n"
).
-spec detect_alert(list(mork@document:block())) -> gleam@option:option(alert()).
detect_alert(Blocks) ->
    case Blocks of
        [{paragraph, _, Inlines} | Rest] ->
            case Inlines of
                [{text, <<"["/utf8>>}, {text, Tag}, {text, <<"]"/utf8>>} | Tail] ->
                    case alert_kind_from_tag(Tag) of
                        {ok, Kind} ->
                            {Title, Body_inlines} = split_alert_first_line(Tail),
                            Body = case Body_inlines of
                                [] ->
                                    Rest;

                                _ ->
                                    [{paragraph, <<""/utf8>>, Body_inlines} |
                                        Rest]
                            end,
                            {some, {alert, Kind, Title, Body}};

                        {error, _} ->
                            none
                    end;

                _ ->
                    none
            end;

        _ ->
            none
    end.

-file("src/spruce/markdown.gleam", 764).
-spec render_list_item_blocks(
    list(mork@document:block()),
    mork@document:list_pack(),
    spruce:spruce(),
    options()
) -> binary().
render_list_item_blocks(Blocks, Pack, Sp, Options) ->
    Separator = case Pack of
        tight ->
            <<"\n"/utf8>>;

        loose ->
            <<"\n\n"/utf8>>
    end,
    _pipe = Blocks,
    _pipe@1 = render_block_list(_pipe, Sp, Options),
    _pipe@2 = remove_empty(_pipe@1),
    gleam@string:join(_pipe@2, Separator).

-file("src/spruce/markdown.gleam", 749).
-spec render_list_labels(
    list(mork@document:list_item()),
    mork@document:list_pack(),
    spruce:spruce(),
    options()
) -> list(binary()).
render_list_labels(Items, Pack, Sp, Options) ->
    case Items of
        [] ->
            [];

        [{list_item, Blocks, _, _} | Rest] ->
            [render_list_item_blocks(Blocks, Pack, Sp, Options) |
                render_list_labels(Rest, Pack, Sp, Options)]
    end.

-file("src/spruce/markdown.gleam", 722).
-spec render_list(
    spruce:spruce(),
    mork@document:list_pack(),
    list(mork@document:list_item()),
    gleam@option:option(integer()),
    options()
) -> binary().
render_list(Sp, Pack, Items, Start, Options) ->
    Labels = render_list_labels(Items, Pack, Sp, Options),
    List_@1 = begin
        _pipe = Labels,
        gleam@list:fold(
            _pipe,
            spruce@list:new(),
            fun(List_, Label) -> spruce@list:item(List_, Label) end
        )
    end,
    case Start of
        none ->
            spruce@list:render(Sp, List_@1);

        {some, Start@1} ->
            List_@2 = begin
                _pipe@1 = List_@1,
                _pipe@2 = spruce@list:kind(_pipe@1, ordered),
                spruce@list:enumerator(
                    _pipe@2,
                    fun(Index, _) ->
                        <<(erlang:integer_to_binary((Start@1 + Index) - 1))/binary,
                            ". "/utf8>>
                    end
                )
            end,
            spruce@list:render(Sp, List_@2)
    end.

-file("src/spruce/markdown.gleam", 321).
-spec render_plain_quote(
    spruce:spruce(),
    list(mork@document:block()),
    options()
) -> binary().
render_plain_quote(Sp, Blocks, Options) ->
    Content = spruce@style:render(
        Sp,
        begin
            _pipe = spruce@style:new(),
            spruce@style:italic(_pipe)
        end,
        render_blocks(Sp, Blocks, Options)
    ),
    Quote_block = begin
        _pipe@1 = spruce@block:new(),
        _pipe@2 = spruce@block:border(_pipe@1, thick),
        _pipe@3 = spruce@block:border_sides(_pipe@2, false, false, false, true),
        _pipe@4 = spruce@block:border_colors(
            _pipe@3,
            erlang:element(18, erlang:element(2, Options)),
            erlang:element(18, erlang:element(2, Options)),
            erlang:element(18, erlang:element(2, Options)),
            erlang:element(18, erlang:element(2, Options))
        ),
        spruce@block:padding(_pipe@4, 0, 0, 0, 1)
    end,
    spruce@block:render(Sp, Content, Quote_block).

-file("src/spruce/markdown.gleam", 429).
-spec render_admonition(spruce:spruce(), alert(), options()) -> binary().
render_admonition(Sp, Alert, Options) ->
    {alert, Kind, Title, Body} = Alert,
    {Color, Icon, Default_title} = alert_properties(Kind),
    Title_text = case Title of
        {some, Inlines} ->
            case gleam@string:trim(render_inlines(Sp, Inlines, Options)) of
                <<""/utf8>> ->
                    Default_title;

                Text ->
                    Text
            end;

        none ->
            Default_title
    end,
    Header = case spruce:supports_color(Sp) of
        true ->
            <<<<(spruce@style:render(
                        Sp,
                        begin
                            _pipe = spruce@style:new(),
                            spruce@style:fg(_pipe, Color)
                        end,
                        Icon
                    ))/binary,
                    " "/utf8>>/binary,
                (spruce@style:render(
                    Sp,
                    begin
                        _pipe@1 = spruce@style:new(),
                        _pipe@2 = spruce@style:bold(_pipe@1),
                        spruce@style:fg(_pipe@2, Color)
                    end,
                    Title_text
                ))/binary>>;

        false ->
            <<<<Icon/binary, " "/utf8>>/binary, Title_text/binary>>
    end,
    Content = case render_blocks(Sp, Body, Options) of
        <<""/utf8>> ->
            Header;

        Body_text ->
            <<<<Header/binary, "\n\n"/utf8>>/binary, Body_text/binary>>
    end,
    Admonition_block = begin
        _pipe@3 = spruce@block:new(),
        _pipe@4 = spruce@block:border(_pipe@3, thick),
        _pipe@5 = spruce@block:border_sides(_pipe@4, false, false, false, true),
        _pipe@6 = spruce@block:border_colors(
            _pipe@5,
            Color,
            Color,
            Color,
            Color
        ),
        spruce@block:padding(_pipe@6, 0, 0, 0, 1)
    end,
    spruce@block:render(Sp, Content, Admonition_block).

-file("src/spruce/markdown.gleam", 310).
-spec render_quote(spruce:spruce(), list(mork@document:block()), options()) -> binary().
render_quote(Sp, Blocks, Options) ->
    case detect_alert(Blocks) of
        {some, Alert} ->
            render_admonition(Sp, Alert, Options);

        none ->
            render_plain_quote(Sp, Blocks, Options)
    end.

-file("src/spruce/markdown.gleam", 243).
-spec render_block(spruce:spruce(), mork@document:block(), options()) -> binary().
render_block(Sp, Block_, Options) ->
    case Block_ of
        {block_quote, Blocks} ->
            render_quote(Sp, Blocks, Options);

        {bullet_list, Pack, Items} ->
            render_list(Sp, Pack, Items, none, Options);

        {code, Lang, Text} ->
            render_code(Sp, Lang, Text, Options);

        empty ->
            <<""/utf8>>;

        {heading, Level, _, Raw, Inlines} ->
            render_heading(Sp, Level, Raw, Inlines, Options);

        {html_block, Raw@1} ->
            render_html_block(Sp, Raw@1, erlang:element(2, Options));

        newline ->
            <<""/utf8>>;

        {ordered_list, Pack@1, Items@1, Start} ->
            render_list(Sp, Pack@1, Items@1, Start, Options);

        {paragraph, _, Inlines@1} ->
            render_paragraph(Sp, Inlines@1, Options);

        {table, Header, Rows} ->
            render_table(Sp, Header, Rows, Options);

        thematic_break ->
            render_rule(Sp, Options)
    end.

-file("src/spruce/markdown.gleam", 229).
-spec render_block_list(list(mork@document:block()), spruce:spruce(), options()) -> list(binary()).
render_block_list(Blocks, Sp, Options) ->
    case Blocks of
        [] ->
            [];

        [First | Rest] ->
            [render_block(Sp, First, Options) |
                render_block_list(Rest, Sp, Options)]
    end.

-file("src/spruce/markdown.gleam", 218).
-spec render_blocks(spruce:spruce(), list(mork@document:block()), options()) -> binary().
render_blocks(Sp, Blocks, Options) ->
    _pipe = Blocks,
    _pipe@1 = render_block_list(_pipe, Sp, Options),
    _pipe@2 = remove_empty(_pipe@1),
    gleam@string:join(_pipe@2, <<"\n\n"/utf8>>).

-file("src/spruce/markdown.gleam", 633).
-spec parse_directive_title(binary()) -> {ok, gleam@option:option(binary())} |
    {error, nil}.
parse_directive_title(After) ->
    case After of
        <<""/utf8>> ->
            {ok, none};

        _ ->
            case gleam_stdlib:string_starts_with(After, <<"["/utf8>>) of
                true ->
                    case gleam@string:split_once(
                        gleam@string:drop_start(After, 1),
                        <<"]"/utf8>>
                    ) of
                        {ok, {Title, Tail}} ->
                            case gleam@string:trim(Tail) of
                                <<""/utf8>> ->
                                    {ok, {some, gleam@string:trim(Title)}};

                                _ ->
                                    {error, nil}
                            end;

                        {error, _} ->
                            {error, nil}
                    end;

                false ->
                    {error, nil}
            end
    end.

-file("src/spruce/markdown.gleam", 664).
-spec is_name_char(binary()) -> boolean().
is_name_char(Char) ->
    case Char of
        <<"a"/utf8>> ->
            true;

        <<"b"/utf8>> ->
            true;

        <<"c"/utf8>> ->
            true;

        <<"d"/utf8>> ->
            true;

        <<"e"/utf8>> ->
            true;

        <<"f"/utf8>> ->
            true;

        <<"g"/utf8>> ->
            true;

        <<"h"/utf8>> ->
            true;

        <<"i"/utf8>> ->
            true;

        <<"j"/utf8>> ->
            true;

        <<"k"/utf8>> ->
            true;

        <<"l"/utf8>> ->
            true;

        <<"m"/utf8>> ->
            true;

        <<"n"/utf8>> ->
            true;

        <<"o"/utf8>> ->
            true;

        <<"p"/utf8>> ->
            true;

        <<"q"/utf8>> ->
            true;

        <<"r"/utf8>> ->
            true;

        <<"s"/utf8>> ->
            true;

        <<"t"/utf8>> ->
            true;

        <<"u"/utf8>> ->
            true;

        <<"v"/utf8>> ->
            true;

        <<"w"/utf8>> ->
            true;

        <<"x"/utf8>> ->
            true;

        <<"y"/utf8>> ->
            true;

        <<"z"/utf8>> ->
            true;

        <<"A"/utf8>> ->
            true;

        <<"B"/utf8>> ->
            true;

        <<"C"/utf8>> ->
            true;

        <<"D"/utf8>> ->
            true;

        <<"E"/utf8>> ->
            true;

        <<"F"/utf8>> ->
            true;

        <<"G"/utf8>> ->
            true;

        <<"H"/utf8>> ->
            true;

        <<"I"/utf8>> ->
            true;

        <<"J"/utf8>> ->
            true;

        <<"K"/utf8>> ->
            true;

        <<"L"/utf8>> ->
            true;

        <<"M"/utf8>> ->
            true;

        <<"N"/utf8>> ->
            true;

        <<"O"/utf8>> ->
            true;

        <<"P"/utf8>> ->
            true;

        <<"Q"/utf8>> ->
            true;

        <<"R"/utf8>> ->
            true;

        <<"S"/utf8>> ->
            true;

        <<"T"/utf8>> ->
            true;

        <<"U"/utf8>> ->
            true;

        <<"V"/utf8>> ->
            true;

        <<"W"/utf8>> ->
            true;

        <<"X"/utf8>> ->
            true;

        <<"Y"/utf8>> ->
            true;

        <<"Z"/utf8>> ->
            true;

        _ ->
            false
    end.

-file("src/spruce/markdown.gleam", 652).
-spec take_name(binary(), binary()) -> {binary(), binary()}.
take_name(Input, Acc) ->
    case gleam_stdlib:string_pop_grapheme(Input) of
        {ok, {Char, Rest}} ->
            case is_name_char(Char) of
                true ->
                    take_name(Rest, <<Acc/binary, Char/binary>>);

                false ->
                    {Acc, Input}
            end;

        {error, _} ->
            {Acc, <<""/utf8>>}
    end.

-file("src/spruce/markdown.gleam", 614).
?DOC(
    " Parse a directive opener such as `:::note` or `:::tip[Custom Title]`,\n"
    " returning the GitHub-alert opener line it maps to.\n"
).
-spec parse_directive_open(binary()) -> {ok, binary()} | {error, nil}.
parse_directive_open(Line) ->
    Trimmed = gleam@string:trim(Line),
    gleam@result:'try'(
        case gleam_stdlib:string_starts_with(Trimmed, <<":::"/utf8>>) of
            true ->
                {ok, gleam@string:drop_start(Trimmed, 3)};

            false ->
                {error, nil}
        end,
        fun(Rest) ->
            {Name, After} = take_name(Rest, <<""/utf8>>),
            gleam@result:'try'(
                alert_kind_from_name(Name),
                fun(_) ->
                    gleam@result:'try'(
                        parse_directive_title(gleam@string:trim(After)),
                        fun(Title) ->
                            Opener = <<<<"> [!"/utf8,
                                    (string:uppercase(Name))/binary>>/binary,
                                "]"/utf8>>,
                            case Title of
                                {some, Title@1} ->
                                    {ok,
                                        <<<<Opener/binary, " "/utf8>>/binary,
                                            Title@1/binary>>};

                                none ->
                                    {ok, Opener}
                            end
                        end
                    )
                end
            )
        end
    ).

-file("src/spruce/markdown.gleam", 591).
-spec count_prefix(binary(), binary(), integer()) -> integer().
count_prefix(Input, Marker, Count) ->
    case gleam_stdlib:string_pop_grapheme(Input) of
        {ok, {Char, Rest}} ->
            case Char =:= Marker of
                true ->
                    count_prefix(Rest, Marker, Count + 1);

                false ->
                    Count
            end;

        {error, _} ->
            Count
    end.

-file("src/spruce/markdown.gleam", 550).
-spec parse_fence_open(binary()) -> gleam@option:option(fence()).
parse_fence_open(Line) ->
    Trimmed = gleam@string:trim(Line),
    case gleam_stdlib:string_pop_grapheme(Trimmed) of
        {ok, {Marker, _}} ->
            case Marker of
                <<"`"/utf8>> ->
                    Length = count_prefix(Trimmed, Marker, 0),
                    case Length >= 3 of
                        true ->
                            {some, {fence, Marker, Length}};

                        false ->
                            none
                    end;

                <<"~"/utf8>> ->
                    Length = count_prefix(Trimmed, Marker, 0),
                    case Length >= 3 of
                        true ->
                            {some, {fence, Marker, Length}};

                        false ->
                            none
                    end;

                _ ->
                    none
            end;

        {error, _} ->
            none
    end.

-file("src/spruce/markdown.gleam", 546).
-spec is_indented_code_line(binary()) -> boolean().
is_indented_code_line(Line) ->
    gleam_stdlib:string_starts_with(Line, <<"    "/utf8>>) orelse gleam_stdlib:string_starts_with(
        Line,
        <<"\t"/utf8>>
    ).

-file("src/spruce/markdown.gleam", 577).
-spec trim_fence_close_candidate(binary(), integer()) -> gleam@option:option(binary()).
trim_fence_close_candidate(Line, Spaces) ->
    case gleam_stdlib:string_pop_grapheme(Line) of
        {ok, {<<" "/utf8>>, Rest}} ->
            case Spaces < 3 of
                true ->
                    trim_fence_close_candidate(Rest, Spaces + 1);

                false ->
                    none
            end;

        {ok, {<<"\t"/utf8>>, _}} ->
            none;

        {ok, _} ->
            {some, gleam@string:trim(Line)};

        {error, _} ->
            none
    end.

-file("src/spruce/markdown.gleam", 569).
-spec closes_fence(binary(), fence()) -> boolean().
closes_fence(Line, Fence) ->
    {fence, Marker, Length} = Fence,
    case trim_fence_close_candidate(Line, 0) of
        {some, Candidate} ->
            count_prefix(Candidate, Marker, 0) >= Length;

        none ->
            false
    end.

-file("src/spruce/markdown.gleam", 607).
-spec quote_line(binary()) -> binary().
quote_line(Line) ->
    gleam@bool:guard(
        Line =:= <<""/utf8>>,
        <<">"/utf8>>,
        fun() -> <<"> "/utf8, Line/binary>> end
    ).

-file("src/spruce/markdown.gleam", 603).
-spec is_directive_close(binary()) -> boolean().
is_directive_close(Line) ->
    gleam@string:trim(Line) =:= <<":::"/utf8>>.

-file("src/spruce/markdown.gleam", 516).
-spec expand_line_outside_directive(
    binary(),
    list(binary()),
    gleam@option:option(fence())
) -> list(binary()).
expand_line_outside_directive(Line, Rest, Fence) ->
    case Fence of
        {some, Fence@1} ->
            Next_fence = case closes_fence(Line, Fence@1) of
                true ->
                    none;

                false ->
                    {some, Fence@1}
            end,
            [Line | expand_lines(Rest, false, Next_fence)];

        none ->
            case is_indented_code_line(Line) of
                true ->
                    [Line | expand_lines(Rest, false, none)];

                false ->
                    case parse_fence_open(Line) of
                        {some, Fence@2} ->
                            [Line | expand_lines(Rest, false, {some, Fence@2})];

                        none ->
                            case parse_directive_open(Line) of
                                {ok, Opener} ->
                                    [Opener | expand_lines(Rest, true, none)];

                                {error, _} ->
                                    [Line | expand_lines(Rest, false, none)]
                            end
                    end
            end
    end.

-file("src/spruce/markdown.gleam", 497).
-spec expand_lines(list(binary()), boolean(), gleam@option:option(fence())) -> list(binary()).
expand_lines(Lines, In_directive, Fence) ->
    case Lines of
        [] ->
            [];

        [Line | Rest] ->
            case In_directive of
                true ->
                    case is_directive_close(Line) of
                        true ->
                            expand_lines(Rest, false, Fence);

                        false ->
                            [quote_line(Line) | expand_lines(Rest, true, Fence)]
                    end;

                false ->
                    expand_line_outside_directive(Line, Rest, Fence)
            end
    end.

-file("src/spruce/markdown.gleam", 486).
?DOC(
    " Rewrite Astro/Starlight `:::type[Title]` … `:::` container directives into\n"
    " GitHub-style `> [!TYPE] Title` alert block quotes, so both syntaxes share\n"
    " one rendering path. Lines that are not recognized directives pass through\n"
    " unchanged.\n"
).
-spec expand_directives(binary()) -> binary().
expand_directives(Markdown) ->
    _pipe = Markdown,
    _pipe@1 = gleam@string:split(_pipe, <<"\n"/utf8>>),
    _pipe@2 = expand_lines(_pipe@1, false, none),
    gleam@string:join(_pipe@2, <<"\n"/utf8>>).

-file("src/spruce/markdown.gleam", 201).
?DOC(" Render Markdown to styled terminal text with explicit options.\n").
-spec render_with(spruce:spruce(), binary(), options()) -> binary().
render_with(Sp, Markdown, Options) ->
    {document, _, Blocks, _, _} = begin
        _pipe = mork:configure(),
        _pipe@1 = mork:tables(_pipe, true),
        _pipe@2 = mork:tasklists(_pipe@1, true),
        _pipe@3 = mork:autolinks(_pipe@2, true),
        _pipe@4 = mork:heading_ids(_pipe@3, true),
        mork:parse_with_options(_pipe@4, expand_directives(Markdown))
    end,
    render_blocks(Sp, Blocks, Options).

-file("src/spruce/markdown.gleam", 196).
?DOC(" Render Markdown to styled terminal text using the default options.\n").
-spec render(spruce:spruce(), binary()) -> binary().
render(Sp, Markdown) ->
    render_with(Sp, Markdown, default_options()).

-file("src/spruce/markdown.gleam", 214).
?DOC(" Render Markdown with the default options and print it to stdout.\n").
-spec print(spruce:spruce(), binary()) -> nil.
print(Sp, Markdown) ->
    gleam_stdlib:println(render(Sp, Markdown)).