Skip to main content

src/spruce@style.erl

-module(spruce@style).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/spruce/style.gleam").
-export([new/0, fg/2, bg/2, complete/3, adaptive/2, bold/1, dim/1, italic/1, underline/1, strikethrough/1, reverse/1, faint/1, inline/1, render/3]).
-export_type([color/0, basic_candidate/0, rgb_value/0, style/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(
    " Composable ANSI styling helpers.\n"
    "\n"
    " A `Style` is an immutable value built with `new` and refined through piped\n"
    " combinators such as `fg`, `bold`, and `underline`. Apply it to text with\n"
    " `render`, which downgrades or drops colors to match the context's color\n"
    " level and resolves adaptive colors against its background.\n"
    "\n"
    " ```gleam\n"
    " import spruce\n"
    " import spruce/style\n"
    "\n"
    " pub fn main() {\n"
    "   let sp = spruce.detect()\n"
    "   let heading = style.new() |> style.fg(style.Cyan) |> style.bold\n"
    "   echo style.render(sp, heading, \"Hello\")\n"
    " }\n"
    " ```\n"
).

-type color() :: black |
    red |
    green |
    yellow |
    blue |
    magenta |
    cyan |
    white |
    gray |
    bright_red |
    bright_green |
    bright_yellow |
    bright_blue |
    bright_magenta |
    bright_cyan |
    bright_white |
    {rgb, integer(), integer(), integer()} |
    {hex, integer()} |
    {ansi256, integer()} |
    {complete, color(), color(), color()} |
    {adaptive, color(), color()}.

-type basic_candidate() :: {basic_candidate, color(), rgb_value()}.

-type rgb_value() :: {rgb_value, integer(), integer(), integer()}.

-opaque style() :: {style,
        gleam@option:option(color()),
        gleam@option:option(color()),
        boolean(),
        boolean(),
        boolean(),
        boolean(),
        boolean(),
        boolean(),
        boolean(),
        boolean()}.

-file("src/spruce/style.gleam", 85).
?DOC(" Create an empty style with no color and no attributes set.\n").
-spec new() -> style().
new() ->
    {style, none, none, false, false, false, false, false, false, false, false}.

-file("src/spruce/style.gleam", 101).
?DOC(" Set the foreground (text) color.\n").
-spec fg(style(), color()) -> style().
fg(Style, Color) ->
    {style,
        {some, Color},
        erlang:element(3, Style),
        erlang:element(4, Style),
        erlang:element(5, Style),
        erlang:element(6, Style),
        erlang:element(7, Style),
        erlang:element(8, Style),
        erlang:element(9, Style),
        erlang:element(10, Style),
        erlang:element(11, Style)}.

-file("src/spruce/style.gleam", 106).
?DOC(" Set the background color.\n").
-spec bg(style(), color()) -> style().
bg(Style, Color) ->
    {style,
        erlang:element(2, Style),
        {some, Color},
        erlang:element(4, Style),
        erlang:element(5, Style),
        erlang:element(6, Style),
        erlang:element(7, Style),
        erlang:element(8, Style),
        erlang:element(9, Style),
        erlang:element(10, Style),
        erlang:element(11, Style)}.

-file("src/spruce/style.gleam", 113).
?DOC(
    " Build a color with an explicit variant per color level: `ansi` for basic\n"
    " terminals, `ansi256` for 256-color, and `truecolor` for truecolor. The\n"
    " level detected in the context selects which variant is used at render time.\n"
).
-spec complete(color(), color(), color()) -> color().
complete(Ansi, Ansi256, Truecolor) ->
    {complete, Ansi, Ansi256, Truecolor}.

-file("src/spruce/style.gleam", 125).
?DOC(
    " Build a color that adapts to the terminal background: `light` is used on a\n"
    " light background, `dark` on a dark (or `Unknown`) background. Each side may\n"
    " be any `Color`, including `complete`, so this also covers\n"
    " \"complete-adaptive\" colors. Resolved against `spruce.background` at render.\n"
).
-spec adaptive(color(), color()) -> color().
adaptive(Light, Dark) ->
    {adaptive, Light, Dark}.

-file("src/spruce/style.gleam", 130).
?DOC(" Enable bold text.\n").
-spec bold(style()) -> style().
bold(Style) ->
    {style,
        erlang:element(2, Style),
        erlang:element(3, Style),
        true,
        erlang:element(5, Style),
        erlang:element(6, Style),
        erlang:element(7, Style),
        erlang:element(8, Style),
        erlang:element(9, Style),
        erlang:element(10, Style),
        erlang:element(11, Style)}.

-file("src/spruce/style.gleam", 135).
?DOC(" Enable dim (faint) text.\n").
-spec dim(style()) -> style().
dim(Style) ->
    {style,
        erlang:element(2, Style),
        erlang:element(3, Style),
        erlang:element(4, Style),
        true,
        erlang:element(6, Style),
        erlang:element(7, Style),
        erlang:element(8, Style),
        erlang:element(9, Style),
        erlang:element(10, Style),
        erlang:element(11, Style)}.

-file("src/spruce/style.gleam", 140).
?DOC(" Enable italic text.\n").
-spec italic(style()) -> style().
italic(Style) ->
    {style,
        erlang:element(2, Style),
        erlang:element(3, Style),
        erlang:element(4, Style),
        erlang:element(5, Style),
        true,
        erlang:element(7, Style),
        erlang:element(8, Style),
        erlang:element(9, Style),
        erlang:element(10, Style),
        erlang:element(11, Style)}.

-file("src/spruce/style.gleam", 145).
?DOC(" Enable underlined text.\n").
-spec underline(style()) -> style().
underline(Style) ->
    {style,
        erlang:element(2, Style),
        erlang:element(3, Style),
        erlang:element(4, Style),
        erlang:element(5, Style),
        erlang:element(6, Style),
        true,
        erlang:element(8, Style),
        erlang:element(9, Style),
        erlang:element(10, Style),
        erlang:element(11, Style)}.

-file("src/spruce/style.gleam", 150).
?DOC(" Enable strikethrough text.\n").
-spec strikethrough(style()) -> style().
strikethrough(Style) ->
    {style,
        erlang:element(2, Style),
        erlang:element(3, Style),
        erlang:element(4, Style),
        erlang:element(5, Style),
        erlang:element(6, Style),
        erlang:element(7, Style),
        true,
        erlang:element(9, Style),
        erlang:element(10, Style),
        erlang:element(11, Style)}.

-file("src/spruce/style.gleam", 155).
?DOC(" Enable reverse video, swapping the foreground and background colors.\n").
-spec reverse(style()) -> style().
reverse(Style) ->
    {style,
        erlang:element(2, Style),
        erlang:element(3, Style),
        erlang:element(4, Style),
        erlang:element(5, Style),
        erlang:element(6, Style),
        erlang:element(7, Style),
        erlang:element(8, Style),
        true,
        erlang:element(10, Style),
        erlang:element(11, Style)}.

-file("src/spruce/style.gleam", 160).
?DOC(" Enable faint text. This is an alias for `dim`.\n").
-spec faint(style()) -> style().
faint(Style) ->
    {style,
        erlang:element(2, Style),
        erlang:element(3, Style),
        erlang:element(4, Style),
        erlang:element(5, Style),
        erlang:element(6, Style),
        erlang:element(7, Style),
        erlang:element(8, Style),
        erlang:element(9, Style),
        true,
        erlang:element(11, Style)}.

-file("src/spruce/style.gleam", 166).
?DOC(
    " Collapse newlines in the text to single spaces when rendering, keeping a\n"
    " styled value on one line.\n"
).
-spec inline(style()) -> style().
inline(Style) ->
    {style,
        erlang:element(2, Style),
        erlang:element(3, Style),
        erlang:element(4, Style),
        erlang:element(5, Style),
        erlang:element(6, Style),
        erlang:element(7, Style),
        erlang:element(8, Style),
        erlang:element(9, Style),
        erlang:element(10, Style),
        true}.

-file("src/spruce/style.gleam", 632).
-spec render_faint(binary(), boolean()) -> binary().
render_faint(Text, Enabled) ->
    gleam@bool:guard(
        not Enabled,
        Text,
        fun() -> gleam_community@ansi:dim(Text) end
    ).

-file("src/spruce/style.gleam", 627).
-spec render_reverse(binary(), boolean()) -> binary().
render_reverse(Text, Enabled) ->
    gleam@bool:guard(
        not Enabled,
        Text,
        fun() -> gleam_community@ansi:inverse(Text) end
    ).

-file("src/spruce/style.gleam", 622).
-spec render_strikethrough(binary(), boolean()) -> binary().
render_strikethrough(Text, Enabled) ->
    gleam@bool:guard(
        not Enabled,
        Text,
        fun() -> gleam_community@ansi:strikethrough(Text) end
    ).

-file("src/spruce/style.gleam", 617).
-spec render_underline(binary(), boolean()) -> binary().
render_underline(Text, Enabled) ->
    gleam@bool:guard(
        not Enabled,
        Text,
        fun() -> gleam_community@ansi:underline(Text) end
    ).

-file("src/spruce/style.gleam", 612).
-spec render_italic(binary(), boolean()) -> binary().
render_italic(Text, Enabled) ->
    gleam@bool:guard(
        not Enabled,
        Text,
        fun() -> gleam_community@ansi:italic(Text) end
    ).

-file("src/spruce/style.gleam", 607).
-spec render_dim(binary(), boolean()) -> binary().
render_dim(Text, Enabled) ->
    gleam@bool:guard(
        not Enabled,
        Text,
        fun() -> gleam_community@ansi:dim(Text) end
    ).

-file("src/spruce/style.gleam", 602).
-spec render_bold(binary(), boolean()) -> binary().
render_bold(Text, Enabled) ->
    gleam@bool:guard(
        not Enabled,
        Text,
        fun() -> gleam_community@ansi:bold(Text) end
    ).

-file("src/spruce/style.gleam", 397).
-spec choose_complete_color(color(), color(), color(), tty:color_level()) -> color().
choose_complete_color(Ansi, Ansi256, Truecolor, Color_level) ->
    case Color_level of
        basic ->
            Ansi;

        ansi256 ->
            Ansi256;

        true_color ->
            Truecolor;

        no_color ->
            Ansi
    end.

-file("src/spruce/style.gleam", 430).
-spec clamp_component(integer()) -> integer().
clamp_component(Value) ->
    gleam@int:clamp(Value, 0, 255).

-file("src/spruce/style.gleam", 425).
-spec clamp_rgb(rgb_value()) -> rgb_value().
clamp_rgb(Rgb) ->
    {rgb_value, R, G, B} = Rgb,
    {rgb_value, clamp_component(R), clamp_component(G), clamp_component(B)}.

-file("src/spruce/style.gleam", 458).
-spec distance_squared(rgb_value(), rgb_value()) -> integer().
distance_squared(Left, Right) ->
    {rgb_value, Left_r, Left_g, Left_b} = clamp_rgb(Left),
    {rgb_value, Right_r, Right_g, Right_b} = clamp_rgb(Right),
    Red_distance = Left_r - Right_r,
    Green_distance = Left_g - Right_g,
    Blue_distance = Left_b - Right_b,
    ((Red_distance * Red_distance) + (Green_distance * Green_distance)) + (Blue_distance
    * Blue_distance).

-file("src/spruce/style.gleam", 453).
-spec candidate_distance(basic_candidate(), rgb_value()) -> integer().
candidate_distance(Candidate, Rgb) ->
    {basic_candidate, _, Candidate_rgb} = Candidate,
    distance_squared(Candidate_rgb, Rgb).

-file("src/spruce/style.gleam", 472).
-spec basic_candidates() -> list(basic_candidate()).
basic_candidates() ->
    [{basic_candidate, black, {rgb_value, 0, 0, 0}},
        {basic_candidate, red, {rgb_value, 128, 0, 0}},
        {basic_candidate, green, {rgb_value, 0, 128, 0}},
        {basic_candidate, yellow, {rgb_value, 128, 128, 0}},
        {basic_candidate, blue, {rgb_value, 0, 0, 128}},
        {basic_candidate, magenta, {rgb_value, 128, 0, 128}},
        {basic_candidate, cyan, {rgb_value, 0, 128, 128}},
        {basic_candidate, white, {rgb_value, 192, 192, 192}},
        {basic_candidate, gray, {rgb_value, 128, 128, 128}},
        {basic_candidate, bright_red, {rgb_value, 255, 0, 0}},
        {basic_candidate, bright_green, {rgb_value, 0, 255, 0}},
        {basic_candidate, bright_yellow, {rgb_value, 255, 255, 0}},
        {basic_candidate, bright_blue, {rgb_value, 0, 0, 255}},
        {basic_candidate, bright_magenta, {rgb_value, 255, 0, 255}},
        {basic_candidate, bright_cyan, {rgb_value, 0, 255, 255}},
        {basic_candidate, bright_white, {rgb_value, 255, 255, 255}}].

-file("src/spruce/style.gleam", 434).
-spec nearest_basic_color(rgb_value()) -> color().
nearest_basic_color(Rgb) ->
    case basic_candidates() of
        [] ->
            black;

        [First | Rest] ->
            Best@1 = gleam@list:fold(
                Rest,
                First,
                fun(Best, Candidate) ->
                    case candidate_distance(Candidate, Rgb) < candidate_distance(
                        Best,
                        Rgb
                    ) of
                        true ->
                            Candidate;

                        false ->
                            Best
                    end
                end
            ),
            {basic_candidate, Color, _} = Best@1,
            Color
    end.

-file("src/spruce/style.gleam", 393).
-spec clamp_ansi256_index(integer()) -> integer().
clamp_ansi256_index(Index) ->
    gleam@int:clamp(Index, 0, 255).

-file("src/spruce/style.gleam", 388).
-spec render_bg_ansi256_sequence(binary(), integer()) -> binary().
render_bg_ansi256_sequence(Text, Index) ->
    Index@1 = clamp_ansi256_index(Index),
    <<<<<<<<"\x{001b}[48;5;"/utf8, (erlang:integer_to_binary(Index@1))/binary>>/binary,
                "m"/utf8>>/binary,
            Text/binary>>/binary,
        "\x{001b}[49m"/utf8>>.

-file("src/spruce/style.gleam", 420).
-spec rgb_to_hex(rgb_value()) -> integer().
rgb_to_hex(Rgb) ->
    {rgb_value, R, G, B} = clamp_rgb(Rgb),
    ((R * 16#10000) + (G * 16#100)) + B.

-file("src/spruce/style.gleam", 515).
-spec ansi256_cube_component(integer()) -> integer().
ansi256_cube_component(Value) ->
    case Value of
        0 ->
            0;

        _ ->
            55 + (Value * 40)
    end.

-file("src/spruce/style.gleam", 522).
-spec ansi256_basic_color(integer()) -> color().
ansi256_basic_color(Index) ->
    case Index of
        0 ->
            black;

        1 ->
            red;

        2 ->
            green;

        3 ->
            yellow;

        4 ->
            blue;

        5 ->
            magenta;

        6 ->
            cyan;

        7 ->
            white;

        8 ->
            gray;

        9 ->
            bright_red;

        10 ->
            bright_green;

        11 ->
            bright_yellow;

        12 ->
            bright_blue;

        13 ->
            bright_magenta;

        14 ->
            bright_cyan;

        _ ->
            bright_white
    end.

-file("src/spruce/style.gleam", 411).
-spec hex_to_rgb(integer()) -> rgb_value().
hex_to_rgb(Value) ->
    Value@1 = gleam@int:clamp(Value, 0, 16#ffffff),
    {rgb_value,
        begin
            _pipe = erlang:'bsr'(Value@1, 16),
            erlang:'band'(_pipe, 16#ff)
        end,
        begin
            _pipe@1 = erlang:'bsr'(Value@1, 8),
            erlang:'band'(_pipe@1, 16#ff)
        end,
        erlang:'band'(Value@1, 16#ff)}.

-file("src/spruce/style.gleam", 543).
-spec basic_color_rgb(color()) -> rgb_value().
basic_color_rgb(Color) ->
    case Color of
        black ->
            {rgb_value, 0, 0, 0};

        red ->
            {rgb_value, 128, 0, 0};

        green ->
            {rgb_value, 0, 128, 0};

        yellow ->
            {rgb_value, 128, 128, 0};

        blue ->
            {rgb_value, 0, 0, 128};

        magenta ->
            {rgb_value, 128, 0, 128};

        cyan ->
            {rgb_value, 0, 128, 128};

        white ->
            {rgb_value, 192, 192, 192};

        gray ->
            {rgb_value, 128, 128, 128};

        bright_red ->
            {rgb_value, 255, 0, 0};

        bright_green ->
            {rgb_value, 0, 255, 0};

        bright_yellow ->
            {rgb_value, 255, 255, 0};

        bright_blue ->
            {rgb_value, 0, 0, 255};

        bright_magenta ->
            {rgb_value, 255, 0, 255};

        bright_cyan ->
            {rgb_value, 0, 255, 255};

        bright_white ->
            {rgb_value, 255, 255, 255};

        {rgb, R, G, B} ->
            {rgb_value, R, G, B};

        {hex, Value} ->
            hex_to_rgb(Value);

        {ansi256, Index} ->
            ansi256_to_rgb(Index);

        {complete, Ansi, _, _} ->
            basic_color_rgb(Ansi);

        {adaptive, _, Dark} ->
            basic_color_rgb(Dark)
    end.

-file("src/spruce/style.gleam", 493).
-spec ansi256_to_rgb(integer()) -> rgb_value().
ansi256_to_rgb(Index) ->
    Index@1 = gleam@int:clamp(Index, 0, 255),
    case Index@1 < 16 of
        true ->
            basic_color_rgb(ansi256_basic_color(Index@1));

        false ->
            case Index@1 < 232 of
                true ->
                    Offset = Index@1 - 16,
                    {rgb_value,
                        ansi256_cube_component(Offset div 36),
                        ansi256_cube_component((Offset rem 36) div 6),
                        ansi256_cube_component(Offset rem 6)};

                false ->
                    Gray = 8 + ((Index@1 - 232) * 10),
                    {rgb_value, Gray, Gray, Gray}
            end
    end.

-file("src/spruce/style.gleam", 592).
-spec rgb_to_ansi256_grayscale_index(rgb_value()) -> integer().
rgb_to_ansi256_grayscale_index(Rgb) ->
    {rgb_value, R, G, B} = clamp_rgb(Rgb),
    Average = ((R + G) + B) div 3,
    232 + gleam@int:clamp(((Average - 8) + 5) div 10, 0, 23).

-file("src/spruce/style.gleam", 598).
-spec quantize_ansi256_cube_channel(integer()) -> integer().
quantize_ansi256_cube_channel(Value) ->
    ((clamp_component(Value) * 5) + 127) div 255.

-file("src/spruce/style.gleam", 582).
-spec rgb_to_ansi256_cube_index(rgb_value()) -> integer().
rgb_to_ansi256_cube_index(Rgb) ->
    {rgb_value, R, G, B} = clamp_rgb(Rgb),
    ((16 + (36 * quantize_ansi256_cube_channel(R))) + (6 * quantize_ansi256_cube_channel(
        G
    )))
    + quantize_ansi256_cube_channel(B).

-file("src/spruce/style.gleam", 569).
-spec rgb_to_ansi256_index(rgb_value()) -> integer().
rgb_to_ansi256_index(Rgb) ->
    Rgb@1 = clamp_rgb(Rgb),
    Cube_index = rgb_to_ansi256_cube_index(Rgb@1),
    Grayscale_index = rgb_to_ansi256_grayscale_index(Rgb@1),
    Cube_rgb = ansi256_to_rgb(Cube_index),
    Grayscale_rgb = ansi256_to_rgb(Grayscale_index),
    case distance_squared(Grayscale_rgb, Rgb@1) < distance_squared(
        Cube_rgb,
        Rgb@1
    ) of
        true ->
            Grayscale_index;

        false ->
            Cube_index
    end.

-file("src/spruce/style.gleam", 369).
-spec render_bg_ansi256(binary(), integer(), tty:color_level()) -> binary().
render_bg_ansi256(Text, Index, Color_level) ->
    Rgb = ansi256_to_rgb(Index),
    case Color_level of
        true_color ->
            gleam_community@ansi:bg_hex(Text, rgb_to_hex(Rgb));

        ansi256 ->
            render_bg_ansi256_sequence(Text, Index);

        basic ->
            render_bg_color(Text, nearest_basic_color(Rgb), Color_level);

        no_color ->
            render_bg_color(Text, nearest_basic_color(Rgb), Color_level)
    end.

-file("src/spruce/style.gleam", 342).
-spec render_bg_rgb(binary(), rgb_value(), tty:color_level()) -> binary().
render_bg_rgb(Text, Rgb, Color_level) ->
    case Color_level of
        true_color ->
            gleam_community@ansi:bg_hex(Text, rgb_to_hex(Rgb));

        ansi256 ->
            render_bg_ansi256_sequence(Text, rgb_to_ansi256_index(Rgb));

        basic ->
            render_bg_color(Text, nearest_basic_color(Rgb), Color_level);

        no_color ->
            render_bg_color(Text, nearest_basic_color(Rgb), Color_level)
    end.

-file("src/spruce/style.gleam", 294).
-spec render_bg_color(binary(), color(), tty:color_level()) -> binary().
render_bg_color(Text, Color, Color_level) ->
    case Color of
        black ->
            gleam_community@ansi:bg_black(Text);

        red ->
            gleam_community@ansi:bg_red(Text);

        green ->
            gleam_community@ansi:bg_green(Text);

        yellow ->
            gleam_community@ansi:bg_yellow(Text);

        blue ->
            gleam_community@ansi:bg_blue(Text);

        magenta ->
            gleam_community@ansi:bg_magenta(Text);

        cyan ->
            gleam_community@ansi:bg_cyan(Text);

        white ->
            gleam_community@ansi:bg_white(Text);

        gray ->
            gleam_community@ansi:bg_bright_black(Text);

        bright_red ->
            gleam_community@ansi:bg_bright_red(Text);

        bright_green ->
            gleam_community@ansi:bg_bright_green(Text);

        bright_yellow ->
            gleam_community@ansi:bg_bright_yellow(Text);

        bright_blue ->
            gleam_community@ansi:bg_bright_blue(Text);

        bright_magenta ->
            gleam_community@ansi:bg_bright_magenta(Text);

        bright_cyan ->
            gleam_community@ansi:bg_bright_cyan(Text);

        bright_white ->
            gleam_community@ansi:bg_bright_white(Text);

        {rgb, R, G, B} ->
            render_bg_rgb(Text, {rgb_value, R, G, B}, Color_level);

        {hex, Value} ->
            render_bg_rgb(Text, hex_to_rgb(Value), Color_level);

        {ansi256, Index} ->
            render_bg_ansi256(Text, Index, Color_level);

        {complete, Ansi, Ansi256, Truecolor} ->
            render_bg_color(
                Text,
                choose_complete_color(Ansi, Ansi256, Truecolor, Color_level),
                Color_level
            );

        {adaptive, _, Dark} ->
            render_bg_color(Text, Dark, Color_level)
    end.

-file("src/spruce/style.gleam", 248).
-spec render_bg(binary(), gleam@option:option(color()), tty:color_level()) -> binary().
render_bg(Text, Color, Color_level) ->
    case Color of
        none ->
            Text;

        {some, Color@1} ->
            render_bg_color(Text, Color@1, Color_level)
    end.

-file("src/spruce/style.gleam", 383).
-spec render_fg_ansi256_sequence(binary(), integer()) -> binary().
render_fg_ansi256_sequence(Text, Index) ->
    Index@1 = clamp_ansi256_index(Index),
    <<<<<<<<"\x{001b}[38;5;"/utf8, (erlang:integer_to_binary(Index@1))/binary>>/binary,
                "m"/utf8>>/binary,
            Text/binary>>/binary,
        "\x{001b}[39m"/utf8>>.

-file("src/spruce/style.gleam", 355).
-spec render_fg_ansi256(binary(), integer(), tty:color_level()) -> binary().
render_fg_ansi256(Text, Index, Color_level) ->
    Rgb = ansi256_to_rgb(Index),
    case Color_level of
        true_color ->
            gleam_community@ansi:hex(Text, rgb_to_hex(Rgb));

        ansi256 ->
            render_fg_ansi256_sequence(Text, Index);

        basic ->
            render_fg_color(Text, nearest_basic_color(Rgb), Color_level);

        no_color ->
            render_fg_color(Text, nearest_basic_color(Rgb), Color_level)
    end.

-file("src/spruce/style.gleam", 329).
-spec render_fg_rgb(binary(), rgb_value(), tty:color_level()) -> binary().
render_fg_rgb(Text, Rgb, Color_level) ->
    case Color_level of
        true_color ->
            gleam_community@ansi:hex(Text, rgb_to_hex(Rgb));

        ansi256 ->
            render_fg_ansi256_sequence(Text, rgb_to_ansi256_index(Rgb));

        basic ->
            render_fg_color(Text, nearest_basic_color(Rgb), Color_level);

        no_color ->
            render_fg_color(Text, nearest_basic_color(Rgb), Color_level)
    end.

-file("src/spruce/style.gleam", 259).
-spec render_fg_color(binary(), color(), tty:color_level()) -> binary().
render_fg_color(Text, Color, Color_level) ->
    case Color of
        black ->
            gleam_community@ansi:black(Text);

        red ->
            gleam_community@ansi:red(Text);

        green ->
            gleam_community@ansi:green(Text);

        yellow ->
            gleam_community@ansi:yellow(Text);

        blue ->
            gleam_community@ansi:blue(Text);

        magenta ->
            gleam_community@ansi:magenta(Text);

        cyan ->
            gleam_community@ansi:cyan(Text);

        white ->
            gleam_community@ansi:white(Text);

        gray ->
            gleam_community@ansi:gray(Text);

        bright_red ->
            gleam_community@ansi:bright_red(Text);

        bright_green ->
            gleam_community@ansi:bright_green(Text);

        bright_yellow ->
            gleam_community@ansi:bright_yellow(Text);

        bright_blue ->
            gleam_community@ansi:bright_blue(Text);

        bright_magenta ->
            gleam_community@ansi:bright_magenta(Text);

        bright_cyan ->
            gleam_community@ansi:bright_cyan(Text);

        bright_white ->
            gleam_community@ansi:bright_white(Text);

        {rgb, R, G, B} ->
            render_fg_rgb(Text, {rgb_value, R, G, B}, Color_level);

        {hex, Value} ->
            render_fg_rgb(Text, hex_to_rgb(Value), Color_level);

        {ansi256, Index} ->
            render_fg_ansi256(Text, Index, Color_level);

        {complete, Ansi, Ansi256, Truecolor} ->
            render_fg_color(
                Text,
                choose_complete_color(Ansi, Ansi256, Truecolor, Color_level),
                Color_level
            );

        {adaptive, _, Dark} ->
            render_fg_color(Text, Dark, Color_level)
    end.

-file("src/spruce/style.gleam", 237).
-spec render_fg(binary(), gleam@option:option(color()), tty:color_level()) -> binary().
render_fg(Text, Color, Color_level) ->
    case Color of
        none ->
            Text;

        {some, Color@1} ->
            render_fg_color(Text, Color@1, Color_level)
    end.

-file("src/spruce/style.gleam", 212).
?DOC(
    " Resolve `Adaptive` colors against the terminal background, treating\n"
    " `Unknown` as `Dark`. Recurses through `Complete` so adaptive colors may nest\n"
    " inside complete colors (and vice versa).\n"
).
-spec resolve_adaptive(color(), tty:background()) -> color().
resolve_adaptive(Color, Background) ->
    case Color of
        {adaptive, Light, Dark} ->
            case Background of
                light ->
                    resolve_adaptive(Light, Background);

                dark ->
                    resolve_adaptive(Dark, Background);

                unknown ->
                    resolve_adaptive(Dark, Background)
            end;

        {complete, Ansi, Ansi256, Truecolor} ->
            {complete,
                resolve_adaptive(Ansi, Background),
                resolve_adaptive(Ansi256, Background),
                resolve_adaptive(Truecolor, Background)};

        Other ->
            Other
    end.

-file("src/spruce/style.gleam", 199).
-spec resolve_adaptive_option(gleam@option:option(color()), tty:background()) -> gleam@option:option(color()).
resolve_adaptive_option(Color, Background) ->
    case Color of
        none ->
            none;

        {some, Color@1} ->
            {some, resolve_adaptive(Color@1, Background)}
    end.

-file("src/spruce/style.gleam", 229).
-spec render_inline(binary(), boolean()) -> binary().
render_inline(Text, Enabled) ->
    gleam@bool:guard(not Enabled, Text, fun() -> _pipe = Text,
            _pipe@1 = gleam@string:replace(_pipe, <<"\r\n"/utf8>>, <<" "/utf8>>),
            _pipe@2 = gleam@string:replace(_pipe@1, <<"\n"/utf8>>, <<" "/utf8>>),
            gleam@string:replace(_pipe@2, <<"\r"/utf8>>, <<" "/utf8>>) end).

-file("src/spruce/style.gleam", 176).
?DOC(
    " Apply a style to `text`, returning the styled string.\n"
    "\n"
    " When the context does not support color, all color and attribute styling is\n"
    " dropped (only the `inline` transform is applied). Colors the terminal cannot\n"
    " represent are downgraded to the nearest available color, and `Adaptive`\n"
    " colors are resolved against the context's background.\n"
).
-spec render(spruce:spruce(), style(), binary()) -> binary().
render(Sp, Style, Text) ->
    Text@1 = render_inline(Text, erlang:element(11, Style)),
    case spruce:supports_color(Sp) of
        false ->
            Text@1;

        true ->
            Background = spruce:background(Sp),
            Fg = resolve_adaptive_option(erlang:element(2, Style), Background),
            Bg = resolve_adaptive_option(erlang:element(3, Style), Background),
            _pipe = Text@1,
            _pipe@1 = render_fg(_pipe, Fg, spruce:color_level(Sp)),
            _pipe@2 = render_bg(_pipe@1, Bg, spruce:color_level(Sp)),
            _pipe@3 = render_bold(_pipe@2, erlang:element(4, Style)),
            _pipe@4 = render_dim(_pipe@3, erlang:element(5, Style)),
            _pipe@5 = render_italic(_pipe@4, erlang:element(6, Style)),
            _pipe@6 = render_underline(_pipe@5, erlang:element(7, Style)),
            _pipe@7 = render_strikethrough(_pipe@6, erlang:element(8, Style)),
            _pipe@8 = render_reverse(_pipe@7, erlang:element(9, Style)),
            render_faint(_pipe@8, erlang:element(10, Style))
    end.