♻ cleanup

This commit is contained in:
Vladislav Nepogodin 2022-01-06 23:23:36 +04:00
parent 1ee447586e
commit a63d00057c
No known key found for this signature in database
GPG Key ID: B62C3D10C54D5DA9
5 changed files with 145 additions and 168 deletions

View File

@ -24,6 +24,7 @@ namespace fs = std::filesystem;
#pragma clang diagnostic ignored "-Wold-style-cast"
#include <range/v3/algorithm/copy.hpp>
#include <range/v3/algorithm/search.hpp>
#pragma clang diagnostic pop
#else
@ -687,16 +688,9 @@ void install_base() noexcept {
// Create the base list of packages
std::vector<std::string> install_packages{};
std::unique_ptr<bool[]> kernels_state{new bool[available_kernels.size()]{false}};
auto kernels{Container::Vertical(detail::from_vector_checklist(available_kernels, kernels_state.get()))};
auto screen = ScreenInteractive::Fullscreen();
auto content = Renderer(kernels, [&] {
return kernels->Render() | center | size(HEIGHT, GREATER_THAN, 10) | size(WIDTH, GREATER_THAN, 40) | vscroll_indicator | yframe | flex;
});
auto screen = ScreenInteractive::Fullscreen();
std::string packages{};
auto ok_callback = [&] {
packages = detail::from_checklist_string(available_kernels, kernels_state.get());
@ -717,30 +711,12 @@ void install_base() noexcept {
screen.ExitLoopClosure()();
};
ButtonOption button_option{.border = false};
auto controls_container = detail::controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
});
static constexpr auto InstStandBseBody = "\nThe base package group will be installed automatically.\nThe base-devel package group is required to use the Arch User Repository (AUR).\n";
static constexpr auto UseSpaceBar = "Use [Spacebar] to de/select options listed.";
const auto& kernels_options_body = fmt::format("\n{}{}\n", InstStandBseBody, UseSpaceBar);
auto global = Container::Vertical({
Renderer([&] { return detail::multiline_text(utils::make_multiline(kernels_options_body)); }),
Renderer([] { return separator(); }),
content,
Renderer([] { return separator(); }),
controls,
});
auto renderer = Renderer(global, [&] {
constexpr auto title = "New CLI Installer | Install Base";
return detail::centered_interative_multi(title, global);
});
screen.Loop(renderer);
constexpr auto base_title = "New CLI Installer | Install Base";
detail::checklist_widget(available_kernels, ok_callback, kernels_state.get(), &screen, kernels_options_body, base_title, {.text_size = nothing});
/* clang-format off */
if (packages.empty()) { return; }
@ -788,16 +764,9 @@ void install_desktop() noexcept {
// Create the base list of packages
std::vector<std::string> install_packages{};
std::unique_ptr<bool[]> des_state{new bool[available_des.size()]{false}};
auto kernels{Container::Vertical(detail::from_vector_checklist(available_des, des_state.get()))};
auto screen = ScreenInteractive::Fullscreen();
auto content = Renderer(kernels, [&] {
return kernels->Render() | center | size(HEIGHT, GREATER_THAN, 10) | size(WIDTH, GREATER_THAN, 40) | vscroll_indicator | yframe | flex;
});
auto screen = ScreenInteractive::Fullscreen();
std::string desktop_env{};
auto ok_callback = [&] {
desktop_env = detail::from_checklist_string(available_des, des_state.get());
@ -805,30 +774,12 @@ void install_desktop() noexcept {
screen.ExitLoopClosure()();
};
ButtonOption button_option{.border = false};
auto controls_container = detail::controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
});
static constexpr auto InstManDEBody = "\nPlease choose a desktop environment.\n";
static constexpr auto UseSpaceBar = "Use [Spacebar] to de/select options listed.";
const auto& kernels_options_body = fmt::format("\n{}{}\n", InstManDEBody, UseSpaceBar);
auto global = Container::Vertical({
Renderer([&] { return detail::multiline_text(utils::make_multiline(kernels_options_body)); }),
Renderer([] { return separator(); }),
content,
Renderer([] { return separator(); }),
controls,
});
const auto& des_options_body = fmt::format("\n{}{}\n", InstManDEBody, UseSpaceBar);
auto renderer = Renderer(global, [&] {
constexpr auto title = "New CLI Installer | Install Desktop";
return detail::centered_interative_multi(title, global);
});
screen.Loop(renderer);
constexpr auto desktop_title = "New CLI Installer | Install Desktop";
detail::checklist_widget(available_des, ok_callback, des_state.get(), &screen, des_options_body, desktop_title, {.text_size = nothing});
/* clang-format off */
if (desktop_env.empty()) { return; }
@ -1122,39 +1073,13 @@ void auto_partition() noexcept {
// Show created partitions
const auto& disk_list = utils::exec(fmt::format("lsblk {} -o NAME,TYPE,FSTYPE,SIZE", device_info));
auto screen = ScreenInteractive::Fullscreen();
/* clang-format off */
auto button_option = ButtonOption();
button_option.border = false;
auto button_back = Button("Back", screen.ExitLoopClosure(), &button_option);
auto container = Container::Horizontal({button_back});
auto renderer = Renderer(container, [&] {
return detail::centered_widget(container, "New CLI Installer", detail::multiline_text(utils::make_multiline(disk_list)) | size(HEIGHT, GREATER_THAN, 5));
});
/* clang-format on */
screen.Loop(renderer);
detail::msgbox_widget(disk_list, size(HEIGHT, GREATER_THAN, 5));
}
// Simple code to show devices / partitions.
void show_devices() noexcept {
auto screen = ScreenInteractive::Fullscreen();
const auto& lsblk = utils::exec("lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE,MOUNTPOINT | grep \"disk\\|part\\|lvm\\|crypt\\|NAME\\|MODEL\\|TYPE\\|FSTYPE\\|SIZE\\|MOUNTPOINT\"");
/* clang-format off */
auto button_option = ButtonOption();
button_option.border = false;
auto button_back = Button("Back", screen.ExitLoopClosure(), &button_option);
auto container = Container::Horizontal({button_back});
auto renderer = Renderer(container, [&] {
return detail::centered_widget(container, "New CLI Installer", detail::multiline_text(utils::make_multiline(lsblk)) | size(HEIGHT, GREATER_THAN, 5));
});
/* clang-format on */
screen.Loop(renderer);
detail::msgbox_widget(lsblk, size(HEIGHT, GREATER_THAN, 5));
}
// This function does not assume that the formatted device is the Root installation device as
@ -1305,42 +1230,19 @@ void mount_opts() noexcept {
/* clang-format on */
}
auto flags = Container::Vertical(detail::from_vector_checklist(fs_opts, fs_opts_state.get()));
auto screen = ScreenInteractive::Fullscreen();
auto content = Renderer(flags, [&] {
return flags->Render() | center | size(HEIGHT, GREATER_THAN, 10) | size(WIDTH, GREATER_THAN, 40) | vscroll_indicator | yframe | flex;
});
auto screen = ScreenInteractive::Fullscreen();
auto& mount_opts_info = std::get<std::string>(config_data["MOUNT_OPTS"]);
auto ok_callback = [&] {
mount_opts_info = detail::from_checklist_string(fs_opts, fs_opts_state.get());
screen.ExitLoopClosure()();
};
ButtonOption button_option{.border = false};
auto controls_container = detail::controls_widget({"OK", "Cancel"}, {ok_callback, screen.ExitLoopClosure()}, &button_option);
const auto& file_sys_formatted = utils::exec(fmt::format("echo {} | sed \"s/.*\\.//g;s/-.*//g\"", file_sys));
const auto& fs_title = fmt::format("New CLI Installer | {}", file_sys_formatted);
const auto& content_size = size(HEIGHT, GREATER_THAN, 10) | size(WIDTH, GREATER_THAN, 40) | vscroll_indicator | yframe | flex;
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
});
std::string mount_options_body = "\nUse [Space] to de/select the desired mount\noptions and review carefully. Please do not\nselect multiple versions of the same option.\n";
auto global = Container::Vertical({
Renderer([&] { return detail::multiline_text(utils::make_multiline(mount_options_body)); }),
Renderer([] { return separator(); }),
content,
Renderer([] { return separator(); }),
controls,
});
auto renderer = Renderer(global, [&] {
const auto& file_sys_formatted = utils::exec(fmt::format("echo {} | sed \"s/.*\\.//g;s/-.*//g\"", file_sys));
const auto& title = fmt::format("New CLI Installer | {}", file_sys_formatted);
return detail::centered_interative_multi(title, global);
});
screen.Loop(renderer);
static constexpr auto mount_options_body = "\nUse [Space] to de/select the desired mount\noptions and review carefully. Please do not\nselect multiple versions of the same option.\n";
detail::checklist_widget(fs_opts, ok_callback, fs_opts_state.get(), &screen, mount_options_body, fs_title, {content_size, nothing});
// Now clean up the file
mount_opts_info = utils::exec(fmt::format("echo \"{}\" | sed \'s/ /,/g\'", mount_opts_info));
@ -1651,7 +1553,7 @@ void make_esp() noexcept {
answer = radiobox_list[static_cast<std::size_t>(selected)];
screen.ExitLoopClosure()();
};
detail::radiolist_widget(radiobox_list, ok_callback, &selected, &screen, MntUefiMessage, detail::WidgetBoxSize{.text_size = nothing});
detail::radiolist_widget(radiobox_list, ok_callback, &selected, &screen, MntUefiMessage, {.text_size = nothing});
}
/* clang-format off */
@ -1824,7 +1726,7 @@ void mount_partitions() noexcept {
if (partition == "Done") {
make_esp();
utils::get_cryptroot();
// get_cryptboot();
utils::get_cryptboot();
return;
}
config_data["MOUNT"] = "";

View File

@ -32,7 +32,6 @@
#include <range/v3/algorithm/for_each.hpp>
#include <range/v3/algorithm/reverse.hpp>
#include <range/v3/algorithm/search.hpp>
#include <range/v3/core.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/split.hpp>
#include <range/v3/view/transform.hpp>
@ -71,7 +70,6 @@ namespace ranges = std::ranges;
namespace fs = std::filesystem;
namespace utils {
static constexpr std::int32_t CONNECTION_TIMEOUT = 15;
bool is_connected() noexcept {
#ifdef NDEVENV
@ -383,60 +381,101 @@ void get_cryptroot() noexcept {
auto& config_data = config_instance->data();
// Identify if /mnt or partition is type "crypt" (LUKS on LVM, or LUKS alone)
if ((utils::exec("lsblk | sed -r 's/^[^[:alnum:]]+//' | awk '/\\/mnt$/ {print $6}' | grep -q crypt", true) == "0")
|| (utils::exec("lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e \"/\\/mnt$/,/part/p\" | awk '{print $6}' | grep -q crypt", true) == "0")) {
config_data["LUKS"] = 1;
auto& luks_name = std::get<std::string>(config_data["LUKS_ROOT_NAME"]);
const auto& luks_dev = std::get<std::string>(config_data["LUKS_DEV"]);
luks_name = utils::exec("mount | awk '/\\/mnt / {print $1}' | sed s~/dev/mapper/~~g | sed s~/dev/~~g");
// Get the name of the Luks device
if (utils::exec("lsblk -i | grep -q -e \"crypt /mnt\"", true) != "0") {
// Mountpoint is not directly on LUKS device, so we need to get the crypt device above the mountpoint
luks_name = utils::exec("lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e \"/\\/mnt$/,/crypt/p\" | awk '/crypt/ {print $1}'");
}
if ((utils::exec("lsblk | sed -r 's/^[^[:alnum:]]+//' | awk '/\\/mnt$/ {print $6}' | grep -q crypt", true) != "0")
|| (utils::exec("lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e \"/\\/mnt$/,/part/p\" | awk '{print $6}' | grep -q crypt", true) != "0")) {
return;
}
const auto& check_cryptparts = [&](const auto cryptparts, auto functor) {
for (const auto& cryptpart : cryptparts) {
if (!utils::exec(fmt::format("lsblk -lno NAME {} | grep \"{}\"", cryptpart, luks_name)).empty()) {
functor(cryptpart);
return true;
}
config_data["LUKS"] = 1;
auto& luks_name = std::get<std::string>(config_data["LUKS_ROOT_NAME"]);
luks_name = utils::exec("mount | awk '/\\/mnt / {print $1}' | sed s~/dev/mapper/~~g | sed s~/dev/~~g");
// Get the name of the Luks device
if (utils::exec("lsblk -i | grep -q -e \"crypt /mnt\"", true) != "0") {
// Mountpoint is not directly on LUKS device, so we need to get the crypt device above the mountpoint
luks_name = utils::exec("lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e \"/\\/mnt$/,/crypt/p\" | awk '/crypt/ {print $1}'");
}
const auto& check_cryptparts = [&luks_name](const auto& cryptparts, auto functor) {
for (const auto& cryptpart : cryptparts) {
if (!utils::exec(fmt::format("lsblk -lno NAME {} | grep \"{}\"", cryptpart, luks_name)).empty()) {
functor(cryptpart);
}
return false;
};
}
};
// Check if LUKS on LVM (parent = lvm /dev/mapper/...)
auto cryptparts = utils::make_multiline(utils::exec("lsblk -lno NAME,FSTYPE,TYPE,MOUNTPOINT | grep \"lvm\" | grep \"/mnt$\" | grep -i \"crypto_luks\" | uniq | awk '{print \"/dev/mapper/\"$1}'"));
auto check_functor = [&](const auto cryptpart) {
// Check if LUKS on LVM (parent = lvm /dev/mapper/...)
auto temp_out = utils::exec("lsblk -lno NAME,FSTYPE,TYPE,MOUNTPOINT | grep \"lvm\" | grep \"/mnt$\" | grep -i \"crypto_luks\" | uniq | awk '{print \"/dev/mapper/\"$1}'");
if (!temp_out.empty()) {
const auto& cryptparts = utils::make_multiline(temp_out);
const auto& check_functor = [&](const auto cryptpart) {
config_data["LUKS_DEV"] = fmt::format("cryptdevice={}:{}", cryptpart, luks_name);
config_data["LVM"] = 1;
};
if (check_cryptparts(cryptparts, check_functor)) {
return;
}
check_cryptparts(cryptparts, check_functor);
return;
}
// Check if LVM on LUKS
cryptparts = utils::make_multiline(utils::exec("lsblk -lno NAME,FSTYPE,TYPE | grep \" crypt$\" | grep -i \"LVM2_member\" | uniq | awk '{print \"/dev/mapper/\"$1}'"));
const auto& check_lvm_luks_dev = [&]([[maybe_unused]] const auto cryptpart) {
// Check if LVM on LUKS
temp_out = utils::exec("lsblk -lno NAME,FSTYPE,TYPE | grep \" crypt$\" | grep -i \"LVM2_member\" | uniq | awk '{print \"/dev/mapper/\"$1}'");
if (!temp_out.empty()) {
const auto& cryptparts = utils::make_multiline(temp_out);
const auto& check_functor = [&]([[maybe_unused]] const auto cryptpart) {
auto& luks_uuid = std::get<std::string>(config_data["LUKS_UUID"]);
luks_uuid = utils::exec("lsblk -ino NAME,FSTYPE,TYPE,MOUNTPOINT,UUID | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e \"/\\/mnt /,/part/p\" | awk '/crypto_LUKS/ {print $4}'");
config_data["LUKS_DEV"] = fmt::format("cryptdevice=UUID={}:{}", luks_uuid, luks_name);
config_data["LVM"] = 1;
};
if (check_cryptparts(cryptparts, check_lvm_luks_dev)) {
return;
}
check_cryptparts(cryptparts, check_functor);
return;
}
// Check if LUKS alone (parent = part /dev/...)
cryptparts = utils::make_multiline(utils::exec("lsblk -lno NAME,FSTYPE,TYPE,MOUNTPOINT | grep \"/mnt$\" | grep \"part\" | grep -i \"crypto_luks\" | uniq | awk '{print \"/dev/\"$1}'"));
const auto& check_func_dev = [&](const auto cryptpart) {
// Check if LUKS alone (parent = part /dev/...)
temp_out = utils::exec("lsblk -lno NAME,FSTYPE,TYPE,MOUNTPOINT | grep \"/mnt$\" | grep \"part\" | grep -i \"crypto_luks\" | uniq | awk '{print \"/dev/\"$1}'");
if (!temp_out.empty()) {
const auto& cryptparts = utils::make_multiline(temp_out);
const auto& check_functor = [&](const auto cryptpart) {
auto& luks_uuid = std::get<std::string>(config_data["LUKS_UUID"]);
luks_uuid = utils::exec(fmt::format("lsblk -lno UUID,TYPE,FSTYPE {} | grep \"part\" | grep -i \"crypto_luks\" | {}", cryptpart, "awk '{print $1}'"));
config_data["LUKS_DEV"] = fmt::format("cryptdevice=UUID={}:{}", luks_uuid, luks_name);
};
if (check_cryptparts(cryptparts, check_func_dev)) {
return;
}
check_cryptparts(cryptparts, check_functor);
return;
}
}
void get_cryptboot() noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
// If /boot is encrypted
if ((utils::exec("lsblk | sed -r 's/^[^[:alnum:]]+//' | awk '/\\/mnt\\/boot$/ {print $6}' | grep -q crypt", true) != "0")
|| (utils::exec("lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e \"/\\/mnt\\/boot$/,/part/p\" | awk '{print $6}' | grep -q crypt", true) != "0")) {
return;
}
config_data["LUKS"] = 1;
// Mountpoint is directly on the LUKS device, so LUKS deivece is the same as root name
std::string boot_name{utils::exec("mount | awk '/\\/mnt\\/boot / {print $1}' | sed s~/dev/mapper/~~g | sed s~/dev/~~g")};
// Get UUID of the encrypted /boot
std::string boot_uuid{utils::exec("lsblk -lno UUID,MOUNTPOINT | awk '/\\mnt\\/boot$/ {print $1}'")};
// Get the name of the Luks device
if (utils::exec("lsblk -i | grep -q -e \"crypt /mnt\"", true) != "0") {
// Mountpoint is not directly on LUKS device, so we need to get the crypt device above the mountpoint
boot_name = utils::exec("lsblk -i | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e \"/\\/mnt\\/boot$/,/crypt/p\" | awk '/crypt/ {print $1}'");
boot_uuid = utils::exec("lsblk -ino NAME,FSTYPE,TYPE,MOUNTPOINT,UUID | tac | sed -r 's/^[^[:alnum:]]+//' | sed -n -e \"/\\/mnt\\/boot /,/part/p\" | awk '/crypto_LUKS/ {print $4}'");
}
// Check if LVM on LUKS
if (utils::exec("lsblk -lno TYPE,MOUNTPOINT | grep \"/mnt/boot$\" | grep -q lvm", true) == "0") {
config_data["LVM"] = 1;
}
// Add cryptdevice to LUKS_DEV, if not already present (if on same LVM on LUKS as /)
auto& luks_dev = std::get<std::string>(config_data["LUKS_DEV"]);
const auto& found = ranges::search(luks_dev, boot_uuid);
if (found.empty()) {
luks_dev = fmt::format("{} cryptdevice=UUID={}:{}", luks_dev, boot_uuid, boot_name);
}
}
@ -538,9 +577,11 @@ void id_system() noexcept {
}
bool handle_connection() noexcept {
bool connected{};
bool connected{utils::is_connected()};
if (!(connected = utils::is_connected())) {
#ifdef NDEVENV
static constexpr std::int32_t CONNECTION_TIMEOUT = 15;
if (!connected) {
warning_inter("An active network connection could not be detected, waiting 15 seconds ...\n");
std::int32_t time_waited{};
@ -568,7 +609,6 @@ bool handle_connection() noexcept {
}
}
#ifdef NDEVENV
if (connected) {
utils::exec("yes | pacman -Sy --noconfirm", true);
}

View File

@ -10,7 +10,7 @@
#include <vector> // for vector
namespace utils {
void print_banner() noexcept;
[[nodiscard]] bool is_connected() noexcept;
bool prompt_char(const char* prompt, const char* color = RESET, char* read = nullptr) noexcept;
void clear_screen() noexcept;

View File

@ -137,15 +137,12 @@ void msgbox_widget(const std::string_view& content, Decorator boxsize) noexcept
auto button_option = ButtonOption();
button_option.border = false;
auto button_back = Button("OK", screen.ExitLoopClosure(), &button_option);
/* clang-format on */
auto container = Container::Horizontal({
button_back,
});
auto container = Container::Horizontal({button_back});
auto renderer = Renderer(container, [&] {
return centered_widget(container, "New CLI Installer", multiline_text(utils::make_multiline(content.data())) | hcenter | boxsize);
return centered_widget(container, "New CLI Installer", multiline_text(utils::make_multiline(content)) | boxsize);
});
/* clang-format on */
screen.Loop(renderer);
}
@ -298,9 +295,10 @@ void radiolist_widget(const std::vector<std::string>& entries, const std::functi
auto radiolist = Container::Vertical({
Radiobox(&entries, selected),
});
auto content = Renderer(radiolist, [&] {
auto content = Renderer(radiolist, [&] {
return radiolist->Render() | center | widget_sizes.content_size;
});
});
ButtonOption button_option{.border = false};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen->ExitLoopClosure()}, &button_option);
@ -332,4 +330,40 @@ void radiolist_widget(const std::vector<std::string>& entries, const std::functi
screen->Loop(renderer);
}
void checklist_widget(const std::vector<std::string>& opts, const std::function<void()>&& ok_callback, bool* opts_state, ScreenInteractive* screen, const std::string_view& text, const std::string_view& title, const WidgetBoxSize widget_sizes) noexcept {
auto checklist{Container::Vertical(detail::from_vector_checklist(opts, opts_state))};
auto content = Renderer(checklist, [&] {
return checklist->Render() | center | widget_sizes.content_size;
});
ButtonOption button_option{.border = false};
auto controls_container = controls_widget({"OK", "Cancel"}, {ok_callback, screen->ExitLoopClosure()}, &button_option);
auto controls = Renderer(controls_container, [&] {
return controls_container->Render() | hcenter | size(HEIGHT, LESS_THAN, 3) | size(WIDTH, GREATER_THAN, 25);
});
Components children{};
if (!text.empty()) {
children = {
Renderer([&] { return detail::multiline_text(utils::make_multiline(text)) | widget_sizes.text_size; }),
Renderer([] { return separator(); }),
content,
Renderer([] { return separator(); }),
controls};
} else {
children = {
content,
Renderer([] { return separator(); }),
controls};
}
auto global{Container::Vertical(children)};
auto renderer = Renderer(global, [&] {
return centered_interative_multi(title, global);
});
screen->Loop(renderer);
}
} // namespace tui::detail

View File

@ -30,13 +30,14 @@ namespace detail {
auto from_vector_checklist(const std::vector<std::string>& opts, bool* opts_state) noexcept -> ftxui::Components;
auto from_checklist_string(const std::vector<std::string>& opts, bool* opts_state) noexcept -> std::string;
auto from_checklist_vector(const std::vector<std::string>& opts, bool* opts_state) noexcept -> std::vector<std::string>;
void msgbox_widget(const std::string_view& content, ftxui::Decorator boxsize = size(ftxui::HEIGHT, ftxui::GREATER_THAN, 5)) noexcept;
void msgbox_widget(const std::string_view& content, ftxui::Decorator boxsize = ftxui::hcenter | size(ftxui::HEIGHT, ftxui::GREATER_THAN, 5)) noexcept;
bool inputbox_widget(std::string& value, const std::string_view& content, ftxui::Decorator boxsize = size(ftxui::HEIGHT, ftxui::GREATER_THAN, 5), bool password = false) noexcept;
void infobox_widget(const std::string_view& content, ftxui::Decorator boxsize = size(ftxui::HEIGHT, ftxui::GREATER_THAN, 5)) noexcept;
bool yesno_widget(const std::string_view& content, ftxui::Decorator boxsize = size(ftxui::HEIGHT, ftxui::GREATER_THAN, 5)) noexcept;
bool yesno_widget(ftxui::Component& container, ftxui::Decorator boxsize = size(ftxui::HEIGHT, ftxui::GREATER_THAN, 5)) noexcept;
void menu_widget(const std::vector<std::string>& entries, const std::function<void()>&& ok_callback, std::int32_t* selected, ftxui::ScreenInteractive* screen, const std::string_view& text = "", const WidgetBoxSize widget_sizes = {}) noexcept;
void radiolist_widget(const std::vector<std::string>& entries, const std::function<void()>&& ok_callback, std::int32_t* selected, ftxui::ScreenInteractive* screen, const std::string_view& text = "", const WidgetBoxSize widget_sizes = {}) noexcept;
void checklist_widget(const std::vector<std::string>& opts, const std::function<void()>&& ok_callback, bool* opts_state, ftxui::ScreenInteractive* screen, const std::string_view& text = "", const std::string_view& title = "New CLI Installer", const WidgetBoxSize widget_sizes = {}) noexcept;
} // namespace detail
} // namespace tui