-module(spruce@layout). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/spruce/layout.gleam"). -export([join_vertical/2, join_horizontal/2, place/5]). -export_type([pos/0, block/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(" ANSI-aware layout helpers for composing multiline text blocks.\n"). -type pos() :: start | center | 'end'. -type block() :: {block, integer(), integer(), list(binary())}. -file("src/spruce/layout.gleam", 110). -spec pad(binary(), integer(), pos()) -> binary(). pad(Text, Width, Pos) -> case Pos of start -> spruce@align:pad_right(Text, Width); center -> spruce@align:pad_center(Text, Width); 'end' -> spruce@align:pad_left(Text, Width) end. -file("src/spruce/layout.gleam", 138). -spec max_visual_width(list(binary())) -> integer(). max_visual_width(Lines) -> _pipe = Lines, _pipe@1 = gleam@list:map(_pipe, fun spruce@align:visual_length/1), gleam@list:fold(_pipe@1, 0, fun gleam@int:max/2). -file("src/spruce/layout.gleam", 131). -spec prepend_reversed(list(binary()), list(binary())) -> list(binary()). prepend_reversed(Lines, Acc) -> case Lines of [] -> Acc; [Line | Rest] -> prepend_reversed(Rest, [Line | Acc]) end. -file("src/spruce/layout.gleam", 123). -spec flatten_lines_loop(list(binary()), list(binary())) -> list(binary()). flatten_lines_loop(Blocks, Acc) -> case Blocks of [] -> Acc; [Block | Rest] -> flatten_lines_loop( Rest, prepend_reversed(gleam@string:split(Block, <<"\n"/utf8>>), Acc) ) end. -file("src/spruce/layout.gleam", 118). -spec flatten_lines(list(binary())) -> list(binary()). flatten_lines(Blocks) -> _pipe = flatten_lines_loop(Blocks, []), lists:reverse(_pipe). -file("src/spruce/layout.gleam", 20). ?DOC(" Stack blocks vertically, padding every line to the widest visual width.\n"). -spec join_vertical(pos(), list(binary())) -> binary(). join_vertical(Pos, Blocks) -> Lines = flatten_lines(Blocks), Width = max_visual_width(Lines), _pipe = Lines, _pipe@1 = gleam@list:map(_pipe, fun(Line) -> pad(Line, Width, Pos) end), gleam@string:join(_pipe@1, <<"\n"/utf8>>). -file("src/spruce/layout.gleam", 183). -spec collect_row(list(list(binary())), binary(), list(list(binary()))) -> {ok, {binary(), list(list(binary()))}} | {error, nil}. collect_row(Blocks, Row, Tails) -> case Blocks of [] -> {ok, {Row, lists:reverse(Tails)}}; [[] | _] -> {error, nil}; [[Line | Rest] | Remaining] -> collect_row(Remaining, <>, [Rest | Tails]) end. -file("src/spruce/layout.gleam", 173). -spec join_normalized_blocks(list(list(binary())), list(binary())) -> binary(). join_normalized_blocks(Blocks, Rows) -> case collect_row(Blocks, <<""/utf8>>, []) of {error, nil} -> _pipe = Rows, _pipe@1 = lists:reverse(_pipe), gleam@string:join(_pipe@1, <<"\n"/utf8>>); {ok, {Row, Tails}} -> join_normalized_blocks(Tails, [Row | Rows]) end. -file("src/spruce/layout.gleam", 162). -spec repeat_line_loop(binary(), integer(), list(binary())) -> list(binary()). repeat_line_loop(Line, Times, Acc) -> case Times =< 0 of true -> Acc; false -> repeat_line_loop(Line, Times - 1, [Line | Acc]) end. -file("src/spruce/layout.gleam", 156). -spec repeat_line(binary(), integer()) -> list(binary()). repeat_line(Line, Times) -> _pipe = repeat_line_loop(Line, Times, []), lists:reverse(_pipe). -file("src/spruce/layout.gleam", 99). -spec padding_counts(integer(), pos()) -> {integer(), integer()}. padding_counts(Extra, Pos) -> case Pos of start -> {0, Extra}; center -> Before = Extra div 2, {Before, Extra - Before}; 'end' -> {Extra, 0} end. -file("src/spruce/layout.gleam", 77). -spec position_lines(list(binary()), integer(), integer(), integer(), pos()) -> list(binary()). position_lines(Lines, Content_height, Width, Target_height, Pos) -> Extra = Target_height - Content_height, case Extra =< 0 of true -> Lines; false -> {Before, After} = padding_counts(Extra, Pos), Blank = gleam@string:repeat(<<" "/utf8>>, Width), _pipe = repeat_line(Blank, Before), _pipe@1 = lists:append(_pipe, Lines), lists:append(_pipe@1, repeat_line(Blank, After)) end. -file("src/spruce/layout.gleam", 70). -spec normalize_block(block(), integer(), pos()) -> list(binary()). normalize_block(Block, Target_height, Pos) -> Padded_lines = gleam@list:map( erlang:element(4, Block), fun(Line) -> pad(Line, erlang:element(2, Block), start) end ), position_lines( Padded_lines, erlang:element(3, Block), erlang:element(2, Block), Target_height, Pos ). -file("src/spruce/layout.gleam", 148). -spec max_block_height_loop(list(block()), integer()) -> integer(). max_block_height_loop(Blocks, Acc) -> case Blocks of [] -> Acc; [{block, _, Height, _} | Rest] -> max_block_height_loop(Rest, gleam@int:max(Acc, Height)) end. -file("src/spruce/layout.gleam", 144). -spec max_block_height(list(block())) -> integer(). max_block_height(Blocks) -> max_block_height_loop(Blocks, 0). -file("src/spruce/layout.gleam", 64). -spec block_info(binary()) -> block(). block_info(Block) -> {Width, _} = spruce@align:size(Block), Height = spruce@align:height(Block), {block, Width, Height, gleam@string:split(Block, <<"\n"/utf8>>)}. -file("src/spruce/layout.gleam", 30). ?DOC(" Join blocks horizontally, aligning shorter blocks within the tallest block.\n"). -spec join_horizontal(pos(), list(binary())) -> binary(). join_horizontal(Pos, Blocks) -> case Blocks of [] -> <<""/utf8>>; _ -> Block_infos = gleam@list:map(Blocks, fun block_info/1), Target_height = max_block_height(Block_infos), _pipe = Block_infos, _pipe@1 = gleam@list:map( _pipe, fun(Block) -> normalize_block(Block, Target_height, Pos) end ), join_normalized_blocks(_pipe@1, []) end. -file("src/spruce/layout.gleam", 45). ?DOC(" Place content in a region, preserving content larger than the requested size.\n"). -spec place(integer(), integer(), pos(), pos(), binary()) -> binary(). place(Width, Height, H, V, Content) -> {Content_width, Content_height} = spruce@align:size(Content), Region_width = gleam@int:max(Width, Content_width), Region_height = gleam@int:max(Height, Content_height), Lines = begin _pipe = Content, _pipe@1 = gleam@string:split(_pipe, <<"\n"/utf8>>), gleam@list:map(_pipe@1, fun(Line) -> pad(Line, Region_width, H) end) end, _pipe@2 = position_lines( Lines, Content_height, Region_width, Region_height, V ), gleam@string:join(_pipe@2, <<"\n"/utf8>>).