-module(spruce@box).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/spruce/box.gleam").
-export([default_options/0, options/2, padding/5, margin/5, border/2, border_colors/5, border_sides/5, width/2, render/3, simple/2, print/2]).
-export_type([border_chars/0, border/0, box_options/0, box_config/0, sides/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(" Boxed terminal output with rounded borders and an optional title.\n").
-type border_chars() :: {border_chars,
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary()}.
-type border() :: normal |
rounded |
thick |
double |
hidden |
block |
{custom, border_chars()}.
-opaque box_options() :: {box_options, binary(), spruce@style:color()} |
{configured_box_options,
binary(),
spruce@style:color(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
border(),
gleam@option:option(integer()),
spruce@style:color(),
spruce@style:color(),
spruce@style:color(),
spruce@style:color(),
boolean(),
boolean(),
boolean(),
boolean()}.
-type box_config() :: {box_config,
binary(),
spruce@style:color(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
border(),
gleam@option:option(integer()),
spruce@style:color(),
spruce@style:color(),
spruce@style:color(),
spruce@style:color(),
boolean(),
boolean(),
boolean(),
boolean()}.
-type sides() :: {sides, boolean(), boolean(), boolean(), boolean()}.
-file("src/spruce/box.gleam", 103).
?DOC(" Default options: no title, cyan border.\n").
-spec default_options() -> box_options().
default_options() ->
{box_options, <<""/utf8>>, cyan}.
-file("src/spruce/box.gleam", 108).
?DOC(" Build options with an explicit title and border color.\n").
-spec options(binary(), spruce@style:color()) -> box_options().
options(Title, Color) ->
{box_options, Title, Color}.
-file("src/spruce/box.gleam", 678).
-spec non_negative(integer()) -> integer().
non_negative(Value) ->
gleam@int:max(0, Value).
-file("src/spruce/box.gleam", 374).
-spec configured(box_config()) -> box_options().
configured(Config) ->
{configured_box_options,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
erlang:element(13, Config),
erlang:element(14, Config),
erlang:element(15, Config),
erlang:element(16, Config),
erlang:element(17, Config),
erlang:element(18, Config),
erlang:element(19, Config),
erlang:element(20, Config),
erlang:element(21, Config)}.
-file("src/spruce/box.gleam", 349).
-spec default_config(binary(), spruce@style:color()) -> box_config().
default_config(Title, Color) ->
{box_config,
Title,
Color,
0,
1,
0,
1,
0,
0,
0,
0,
rounded,
none,
Color,
Color,
Color,
Color,
true,
true,
true,
true}.
-file("src/spruce/box.gleam", 299).
-spec box_config(box_options()) -> box_config().
box_config(Options) ->
case Options of
{box_options, Title, Color} ->
default_config(Title, Color);
{configured_box_options,
Title@1,
Color@1,
Padding_top,
Padding_right,
Padding_bottom,
Padding_left,
Margin_top,
Margin_right,
Margin_bottom,
Margin_left,
Border,
Width,
Border_top_color,
Border_right_color,
Border_bottom_color,
Border_left_color,
Border_top,
Border_right,
Border_bottom,
Border_left} ->
{box_config,
Title@1,
Color@1,
Padding_top,
Padding_right,
Padding_bottom,
Padding_left,
Margin_top,
Margin_right,
Margin_bottom,
Margin_left,
Border,
Width,
Border_top_color,
Border_right_color,
Border_bottom_color,
Border_left_color,
Border_top,
Border_right,
Border_bottom,
Border_left}
end.
-file("src/spruce/box.gleam", 113).
?DOC(" Set inner padding as top, right, bottom, left cell counts.\n").
-spec padding(box_options(), integer(), integer(), integer(), integer()) -> box_options().
padding(Options, Top, Right, Bottom, Left) ->
Config = box_config(Options),
configured(
{box_config,
erlang:element(2, Config),
erlang:element(3, Config),
non_negative(Top),
non_negative(Right),
non_negative(Bottom),
non_negative(Left),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
erlang:element(13, Config),
erlang:element(14, Config),
erlang:element(15, Config),
erlang:element(16, Config),
erlang:element(17, Config),
erlang:element(18, Config),
erlang:element(19, Config),
erlang:element(20, Config),
erlang:element(21, Config)}
).
-file("src/spruce/box.gleam", 134).
?DOC(" Set outer margin as top, right, bottom, left cell counts.\n").
-spec margin(box_options(), integer(), integer(), integer(), integer()) -> box_options().
margin(Options, Top, Right, Bottom, Left) ->
Config = box_config(Options),
configured(
{box_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
non_negative(Top),
non_negative(Right),
non_negative(Bottom),
non_negative(Left),
erlang:element(12, Config),
erlang:element(13, Config),
erlang:element(14, Config),
erlang:element(15, Config),
erlang:element(16, Config),
erlang:element(17, Config),
erlang:element(18, Config),
erlang:element(19, Config),
erlang:element(20, Config),
erlang:element(21, Config)}
).
-file("src/spruce/box.gleam", 155).
?DOC(" Set the border style.\n").
-spec border(box_options(), border()) -> box_options().
border(Options, Border) ->
Config = box_config(Options),
configured(
{box_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
Border,
erlang:element(13, Config),
erlang:element(14, Config),
erlang:element(15, Config),
erlang:element(16, Config),
erlang:element(17, Config),
erlang:element(18, Config),
erlang:element(19, Config),
erlang:element(20, Config),
erlang:element(21, Config)}
).
-file("src/spruce/box.gleam", 161).
?DOC(" Set top, right, bottom, and left border colors independently.\n").
-spec border_colors(
box_options(),
spruce@style:color(),
spruce@style:color(),
spruce@style:color(),
spruce@style:color()
) -> box_options().
border_colors(Options, Top, Right, Bottom, Left) ->
Config = box_config(Options),
configured(
{box_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
erlang:element(13, Config),
Top,
Right,
Bottom,
Left,
erlang:element(18, Config),
erlang:element(19, Config),
erlang:element(20, Config),
erlang:element(21, Config)}
).
-file("src/spruce/box.gleam", 182).
?DOC(" Set top, right, bottom, and left border visibility independently.\n").
-spec border_sides(box_options(), boolean(), boolean(), boolean(), boolean()) -> box_options().
border_sides(Options, Top, Right, Bottom, Left) ->
Config = box_config(Options),
configured(
{box_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
erlang:element(13, Config),
erlang:element(14, Config),
erlang:element(15, Config),
erlang:element(16, Config),
erlang:element(17, Config),
Top,
Right,
Bottom,
Left}
).
-file("src/spruce/box.gleam", 203).
?DOC(" Constrain content to a visual width, wrapping with `spruce/align.wrap`.\n").
-spec width(box_options(), integer()) -> box_options().
width(Options, Width) ->
Config = box_config(Options),
configured(
{box_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config),
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config),
erlang:element(12, Config),
{some, non_negative(Width)},
erlang:element(14, Config),
erlang:element(15, Config),
erlang:element(16, Config),
erlang:element(17, Config),
erlang:element(18, Config),
erlang:element(19, Config),
erlang:element(20, Config),
erlang:element(21, Config)}
).
-file("src/spruce/box.gleam", 667).
-spec repeat_line_loop(binary(), integer(), list(binary())) -> list(binary()).
repeat_line_loop(Line, Count, Acc) ->
case Count of
0 ->
Acc;
_ ->
repeat_line_loop(Line, Count - 1, [Line | Acc])
end.
-file("src/spruce/box.gleam", 663).
-spec repeat_line(binary(), integer()) -> list(binary()).
repeat_line(Line, Count) ->
repeat_line_loop(Line, non_negative(Count), []).
-file("src/spruce/box.gleam", 657).
-spec find_max_width(list(binary()), integer()) -> integer().
find_max_width(Lines, Min_width) ->
_pipe = Lines,
_pipe@1 = gleam@list:map(_pipe, fun spruce@align:visual_length/1),
gleam@list:fold(_pipe@1, Min_width, fun gleam@int:max/2).
-file("src/spruce/box.gleam", 633).
-spec apply_margin(
list(binary()),
binary(),
integer(),
integer(),
integer(),
integer()
) -> list(binary()).
apply_margin(Lines, Prefix, Top, Right, Bottom, Left) ->
Line_width = find_max_width(Lines, 0),
Blank = gleam@string:repeat(<<" "/utf8>>, (Left + Line_width) + Right),
Top_rows = repeat_line(<<Prefix/binary, Blank/binary>>, Top),
Body = gleam@list:map(
Lines,
fun(Line) ->
<<<<<<Prefix/binary,
(gleam@string:repeat(<<" "/utf8>>, Left))/binary>>/binary,
(spruce@align:pad_right(Line, Line_width))/binary>>/binary,
(gleam@string:repeat(<<" "/utf8>>, Right))/binary>>
end
),
_pipe = Top_rows,
_pipe@1 = lists:append(_pipe, Body),
lists:append(_pipe@1, repeat_line(<<Prefix/binary, Blank/binary>>, Bottom)).
-file("src/spruce/box.gleam", 547).
-spec render_bottom(
integer(),
sides(),
border_chars(),
fun((binary()) -> binary())
) -> binary().
render_bottom(Inner_width, Sides, Chars, Paint_bottom) ->
Left = case erlang:element(5, Sides) of
true ->
erlang:element(8, Chars);
false ->
<<""/utf8>>
end,
Right = case erlang:element(3, Sides) of
true ->
erlang:element(6, Chars);
false ->
<<""/utf8>>
end,
Paint_bottom(
<<<<Left/binary,
(gleam@string:repeat(erlang:element(7, Chars), Inner_width))/binary>>/binary,
Right/binary>>
).
-file("src/spruce/box.gleam", 504).
-spec append_bottom(
list(binary()),
sides(),
integer(),
border_chars(),
fun((binary()) -> binary())
) -> list(binary()).
append_bottom(Lines, Sides, Inner_width, Chars, Paint_bottom) ->
gleam@bool:guard(
erlang:element(4, Sides) =:= false,
Lines,
fun() ->
lists:append(
Lines,
[render_bottom(Inner_width, Sides, Chars, Paint_bottom)]
)
end
).
-file("src/spruce/box.gleam", 516).
-spec render_top(
binary(),
integer(),
integer(),
sides(),
border_chars(),
fun((binary()) -> binary())
) -> binary().
render_top(Title, Title_width, Inner_width, Sides, Chars, Paint_top) ->
Left = case erlang:element(5, Sides) of
true ->
erlang:element(2, Chars);
false ->
<<""/utf8>>
end,
Right = case erlang:element(3, Sides) of
true ->
erlang:element(4, Chars);
false ->
<<""/utf8>>
end,
case Title of
<<""/utf8>> ->
Paint_top(
<<<<Left/binary,
(gleam@string:repeat(
erlang:element(3, Chars),
Inner_width
))/binary>>/binary,
Right/binary>>
);
Title@1 ->
<<<<<<<<(Paint_top(
<<Left/binary,
(erlang:element(3, Chars))/binary>>
))/binary,
" "/utf8>>/binary,
Title@1/binary>>/binary,
" "/utf8>>/binary,
(Paint_top(
<<(gleam@string:repeat(
erlang:element(3, Chars),
gleam@int:max(0, (Inner_width - Title_width) - 3)
))/binary,
Right/binary>>
))/binary>>
end.
-file("src/spruce/box.gleam", 623).
-spec side_right(sides(), border_chars(), fun((binary()) -> binary())) -> binary().
side_right(Sides, Chars, Paint_right) ->
gleam@bool:guard(
erlang:element(3, Sides) =:= false,
<<""/utf8>>,
fun() -> Paint_right(erlang:element(5, Chars)) end
).
-file("src/spruce/box.gleam", 613).
-spec side_left(sides(), border_chars(), fun((binary()) -> binary())) -> binary().
side_left(Sides, Chars, Paint_left) ->
gleam@bool:guard(
erlang:element(5, Sides) =:= false,
<<""/utf8>>,
fun() -> Paint_left(erlang:element(9, Chars)) end
).
-file("src/spruce/box.gleam", 599).
-spec render_blank_row(
integer(),
sides(),
border_chars(),
fun((binary()) -> binary()),
fun((binary()) -> binary())
) -> binary().
render_blank_row(Inner_width, Sides, Chars, Paint_left, Paint_right) ->
Inner = gleam@string:repeat(<<" "/utf8>>, Inner_width),
<<<<(side_left(Sides, Chars, Paint_left))/binary, Inner/binary>>/binary,
(side_right(Sides, Chars, Paint_right))/binary>>.
-file("src/spruce/box.gleam", 585).
-spec blank_rows(
integer(),
integer(),
sides(),
border_chars(),
fun((binary()) -> binary()),
fun((binary()) -> binary())
) -> list(binary()).
blank_rows(Count, Inner_width, Sides, Chars, Paint_left, Paint_right) ->
repeat_line(
render_blank_row(Inner_width, Sides, Chars, Paint_left, Paint_right),
Count
).
-file("src/spruce/box.gleam", 565).
-spec render_body_line(
binary(),
integer(),
integer(),
integer(),
sides(),
border_chars(),
fun((binary()) -> binary()),
fun((binary()) -> binary())
) -> binary().
render_body_line(
Line,
Text_width,
Padding_left,
Padding_right,
Sides,
Chars,
Paint_left,
Paint_right
) ->
Inner = <<<<(gleam@string:repeat(<<" "/utf8>>, Padding_left))/binary,
(spruce@align:pad_right(Line, Text_width))/binary>>/binary,
(gleam@string:repeat(<<" "/utf8>>, Padding_right))/binary>>,
<<<<(side_left(Sides, Chars, Paint_left))/binary, Inner/binary>>/binary,
(side_right(Sides, Chars, Paint_right))/binary>>.
-file("src/spruce/box.gleam", 498).
-spec border_painter(spruce:spruce(), spruce@style:color()) -> fun((binary()) -> binary()).
border_painter(Sp, Color) ->
Border_style = begin
_pipe = spruce@style:new(),
spruce@style:fg(_pipe, Color)
end,
fun(Text) -> spruce@style:render(Sp, Border_style, Text) end.
-file("src/spruce/box.gleam", 412).
-spec border_chars(border()) -> border_chars().
border_chars(Border) ->
case Border of
normal ->
{border_chars,
<<"┌"/utf8>>,
<<"─"/utf8>>,
<<"┐"/utf8>>,
<<"│"/utf8>>,
<<"┘"/utf8>>,
<<"─"/utf8>>,
<<"└"/utf8>>,
<<"│"/utf8>>};
rounded ->
{border_chars,
<<"╭"/utf8>>,
<<"─"/utf8>>,
<<"╮"/utf8>>,
<<"│"/utf8>>,
<<"╯"/utf8>>,
<<"─"/utf8>>,
<<"╰"/utf8>>,
<<"│"/utf8>>};
thick ->
{border_chars,
<<"┏"/utf8>>,
<<"━"/utf8>>,
<<"┓"/utf8>>,
<<"┃"/utf8>>,
<<"┛"/utf8>>,
<<"━"/utf8>>,
<<"┗"/utf8>>,
<<"┃"/utf8>>};
double ->
{border_chars,
<<"╔"/utf8>>,
<<"═"/utf8>>,
<<"╗"/utf8>>,
<<"║"/utf8>>,
<<"╝"/utf8>>,
<<"═"/utf8>>,
<<"╚"/utf8>>,
<<"║"/utf8>>};
block ->
{border_chars,
<<"█"/utf8>>,
<<"█"/utf8>>,
<<"█"/utf8>>,
<<"█"/utf8>>,
<<"█"/utf8>>,
<<"█"/utf8>>,
<<"█"/utf8>>,
<<"█"/utf8>>};
hidden ->
{border_chars,
<<""/utf8>>,
<<""/utf8>>,
<<""/utf8>>,
<<""/utf8>>,
<<""/utf8>>,
<<""/utf8>>,
<<""/utf8>>,
<<""/utf8>>};
{custom, Chars} ->
Chars
end.
-file("src/spruce/box.gleam", 491).
-spec sanitize_title(binary()) -> binary().
sanitize_title(Title) ->
_pipe = Title,
_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>>).
-file("src/spruce/box.gleam", 484).
-spec wrap_content(binary(), gleam@option:option(integer())) -> binary().
wrap_content(Content, Width) ->
case Width of
{some, Width@1} when Width@1 > 0 ->
spruce@align:wrap(Content, Width@1);
_ ->
Content
end.
-file("src/spruce/box.gleam", 399).
-spec effective_sides(box_config()) -> sides().
effective_sides(Config) ->
case erlang:element(12, Config) of
hidden ->
{sides, false, false, false, false};
_ ->
{sides,
erlang:element(18, Config),
erlang:element(19, Config),
erlang:element(20, Config),
erlang:element(21, Config)}
end.
-file("src/spruce/box.gleam", 214).
?DOC(" Render `content` in a box with the given options.\n").
-spec render(spruce:spruce(), binary(), box_options()) -> binary().
render(Sp, Content, Options) ->
Config = box_config(Options),
Prefix = spruce@internal@layout:indent_prefix(Sp),
Sides = effective_sides(Config),
Lines = begin
_pipe = Content,
_pipe@1 = wrap_content(_pipe, erlang:element(13, Config)),
gleam@string:split(_pipe@1, <<"\n"/utf8>>)
end,
Title = sanitize_title(erlang:element(2, Config)),
Title_width = spruce@align:visual_length(Title),
Title_min_width = case Title of
<<""/utf8>> ->
0;
_ ->
Title_width + 3
end,
Content_width = find_max_width(Lines, 0),
Padded_content_width = (Content_width + erlang:element(7, Config)) + erlang:element(
5,
Config
),
Inner_width = gleam@int:max(Padded_content_width, Title_min_width),
Text_width = (Inner_width - erlang:element(7, Config)) - erlang:element(
5,
Config
),
Chars = border_chars(erlang:element(12, Config)),
Paint_top = border_painter(Sp, erlang:element(14, Config)),
Paint_right = border_painter(Sp, erlang:element(15, Config)),
Paint_bottom = border_painter(Sp, erlang:element(16, Config)),
Paint_left = border_painter(Sp, erlang:element(17, Config)),
Body = lists:append(
blank_rows(
erlang:element(4, Config),
Inner_width,
Sides,
Chars,
Paint_left,
Paint_right
),
begin
Content_rows = gleam@list:map(
Lines,
fun(Line) ->
render_body_line(
Line,
Text_width,
erlang:element(7, Config),
erlang:element(5, Config),
Sides,
Chars,
Paint_left,
Paint_right
)
end
),
lists:append(
Content_rows,
blank_rows(
erlang:element(6, Config),
Inner_width,
Sides,
Chars,
Paint_left,
Paint_right
)
)
end
),
Boxed = begin
_pipe@2 = case erlang:element(2, Sides) of
true ->
[render_top(
Title,
Title_width,
Inner_width,
Sides,
Chars,
Paint_top
) |
Body];
false ->
Body
end,
append_bottom(_pipe@2, Sides, Inner_width, Chars, Paint_bottom)
end,
_pipe@3 = Boxed,
_pipe@4 = apply_margin(
_pipe@3,
Prefix,
erlang:element(8, Config),
erlang:element(9, Config),
erlang:element(10, Config),
erlang:element(11, Config)
),
gleam@string:join(_pipe@4, <<"\n"/utf8>>).
-file("src/spruce/box.gleam", 209).
?DOC(" Render `content` in a box using the default options.\n").
-spec simple(spruce:spruce(), binary()) -> binary().
simple(Sp, Content) ->
render(Sp, Content, default_options()).
-file("src/spruce/box.gleam", 683).
?DOC(" Render a default box and print it to stdout.\n").
-spec print(spruce:spruce(), binary()) -> nil.
print(Sp, Content) ->
gleam_stdlib:println(simple(Sp, Content)).