refactor: overhaul color mechanics

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
This commit is contained in:
dw-0
2024-10-26 00:02:37 +02:00
parent 106bf7675f
commit e63d9d67ec
35 changed files with 377 additions and 423 deletions

View File

@@ -30,7 +30,7 @@ from core.backup_manager.backup_manager import BackupManager
from core.constants import CURRENT_USER from core.constants import CURRENT_USER
from core.logger import DialogType, Logger from core.logger import DialogType, Logger
from core.settings.kiauh_settings import KiauhSettings from core.settings.kiauh_settings import KiauhSettings
from core.types import ComponentStatus from core.types.component_status import ComponentStatus
from utils.common import ( from utils.common import (
check_install_dependencies, check_install_dependencies,
get_install_status, get_install_status,

View File

@@ -11,13 +11,8 @@ import textwrap
from enum import Enum, unique from enum import Enum, unique
from typing import List from typing import List
from core.constants import (
COLOR_CYAN,
COLOR_GREEN,
COLOR_YELLOW,
RESET_FORMAT,
)
from core.menus.base_menu import print_back_footer from core.menus.base_menu import print_back_footer
from core.types.color import Color
from utils.instance_type import InstanceType from utils.instance_type import InstanceType
@@ -42,12 +37,12 @@ def print_instance_overview(
if display_type is DisplayType.SERVICE_NAME if display_type is DisplayType.SERVICE_NAME
else "printer directories" else "printer directories"
) )
headline = f"{COLOR_GREEN}The following {d_type} were found:{RESET_FORMAT}" headline = Color.apply(f"The following {d_type} were found:", Color.GREEN)
dialog += f"{headline:^64}\n" dialog += f"{headline:^64}\n"
dialog += "╟───────────────────────────────────────────────────────╢\n" dialog += "╟───────────────────────────────────────────────────────╢\n"
if show_select_all: if show_select_all:
select_all = f"{COLOR_YELLOW}a) Select all{RESET_FORMAT}" select_all = Color.apply("a) Select all", Color.YELLOW)
dialog += f"{select_all:<63}\n" dialog += f"{select_all:<63}\n"
dialog += "║ ║\n" dialog += "║ ║\n"
@@ -56,7 +51,9 @@ def print_instance_overview(
name = s.service_file_path.stem name = s.service_file_path.stem
else: else:
name = s.data_dir name = s.data_dir
line = f"{COLOR_CYAN}{f'{i + start_index})' if show_index else ''} {name}{RESET_FORMAT}" line = Color.apply(
f"{f'{i + start_index})' if show_index else ''} {name}", Color.CYAN
)
dialog += f"{line:<63}\n" dialog += f"{line:<63}\n"
dialog += "╟───────────────────────────────────────────────────────╢\n" dialog += "╟───────────────────────────────────────────────────────╢\n"
@@ -65,8 +62,10 @@ def print_instance_overview(
def print_select_instance_count_dialog() -> None: def print_select_instance_count_dialog() -> None:
line1 = f"{COLOR_YELLOW}WARNING:{RESET_FORMAT}" line1 = Color.apply("WARNING:", Color.YELLOW)
line2 = f"{COLOR_YELLOW}Setting up too many instances may crash your system.{RESET_FORMAT}" line2 = Color.apply(
"Setting up too many instances may crash your system.", Color.YELLOW
)
dialog = textwrap.dedent( dialog = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗ ╔═══════════════════════════════════════════════════════╗
@@ -85,8 +84,8 @@ def print_select_instance_count_dialog() -> None:
def print_select_custom_name_dialog() -> None: def print_select_custom_name_dialog() -> None:
line1 = f"{COLOR_YELLOW}INFO:{RESET_FORMAT}" line1 = Color.apply("INFO:", Color.YELLOW)
line2 = f"{COLOR_YELLOW}Only alphanumeric characters are allowed!{RESET_FORMAT}" line2 = Color.apply("Only alphanumeric characters are allowed!", Color.YELLOW)
dialog = textwrap.dedent( dialog = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗ ╔═══════════════════════════════════════════════════════╗

View File

@@ -36,7 +36,7 @@ from core.logger import DialogType, Logger
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import ( from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
SimpleConfigParser, SimpleConfigParser,
) )
from core.types import ComponentStatus from core.types.component_status import ComponentStatus
from utils.common import get_install_status from utils.common import get_install_status
from utils.input_utils import get_confirm, get_number_input, get_string_input from utils.input_utils import get_confirm, get_number_input, get_string_input
from utils.instance_utils import get_instances from utils.instance_utils import get_instances

View File

@@ -12,15 +12,17 @@ import textwrap
from typing import Type from typing import Type
from components.klipper import klipper_remove from components.klipper import klipper_remove
from core.constants import COLOR_CYAN, COLOR_RED, RESET_FORMAT
from core.menus import FooterType, Option from core.menus import FooterType, Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.types.color import Color
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
class KlipperRemoveMenu(BaseMenu): class KlipperRemoveMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "Remove Klipper"
self.title_color = Color.RED
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.footer_type = FooterType.BACK self.footer_type = FooterType.BACK
self.remove_klipper_service = False self.remove_klipper_service = False
@@ -43,18 +45,13 @@ class KlipperRemoveMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ Remove Klipper ] " checked = f"[{Color.apply('x', Color.CYAN)}]"
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
checked = f"[{COLOR_CYAN}x{RESET_FORMAT}]"
unchecked = "[ ]" unchecked = "[ ]"
o1 = checked if self.remove_klipper_service else unchecked o1 = checked if self.remove_klipper_service else unchecked
o2 = checked if self.remove_klipper_dir else unchecked o2 = checked if self.remove_klipper_dir else unchecked
o3 = checked if self.remove_klipper_env else unchecked o3 = checked if self.remove_klipper_env else unchecked
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ Enter a number and hit enter to select / deselect ║ ║ Enter a number and hit enter to select / deselect ║
║ the specific option for removal. ║ ║ the specific option for removal. ║
@@ -92,8 +89,11 @@ class KlipperRemoveMenu(BaseMenu):
and not self.remove_klipper_dir and not self.remove_klipper_dir
and not self.remove_klipper_env and not self.remove_klipper_env
): ):
error = f"{COLOR_RED}Nothing selected! Select options to remove first.{RESET_FORMAT}" print(
print(error) Color.apply(
"Nothing selected! Select options to remove first.", Color.RED
)
)
return return
klipper_remove.run_klipper_removal( klipper_remove.run_klipper_removal(

View File

@@ -17,10 +17,10 @@ from components.klipper_firmware.firmware_utils import (
run_make_clean, run_make_clean,
run_make_menuconfig, run_make_menuconfig,
) )
from core.constants import COLOR_CYAN, COLOR_GREEN, COLOR_RED, RESET_FORMAT
from core.logger import Logger from core.logger import Logger
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.types.color import Color
from utils.sys_utils import ( from utils.sys_utils import (
check_package_install, check_package_install,
install_system_packages, install_system_packages,
@@ -33,6 +33,8 @@ from utils.sys_utils import (
class KlipperBuildFirmwareMenu(BaseMenu): class KlipperBuildFirmwareMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "Build Firmware Menu"
self.title_color = Color.CYAN
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.deps: Set[str] = {"build-essential", "dpkg-dev", "make"} self.deps: Set[str] = {"build-essential", "dpkg-dev", "make"}
self.missing_deps: List[str] = check_package_install(self.deps) self.missing_deps: List[str] = check_package_install(self.deps)
@@ -53,13 +55,8 @@ class KlipperBuildFirmwareMenu(BaseMenu):
self.default_option = Option(method=self.install_missing_deps) self.default_option = Option(method=self.install_missing_deps)
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ Build Firmware Menu ] "
color = COLOR_CYAN
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent( menu = textwrap.dedent(
f""" """
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ The following dependencies are required: ║ ║ The following dependencies are required: ║
║ ║ ║ ║
@@ -67,20 +64,22 @@ class KlipperBuildFirmwareMenu(BaseMenu):
)[1:] )[1:]
for d in self.deps: for d in self.deps:
status_ok = f"{COLOR_GREEN}*INSTALLED*{RESET_FORMAT}" status_ok = Color.apply("*INSTALLED*", Color.GREEN)
status_missing = f"{COLOR_RED}*MISSING*{RESET_FORMAT}" status_missing = Color.apply("*MISSING*", Color.RED)
status = status_missing if d in self.missing_deps else status_ok status = status_missing if d in self.missing_deps else status_ok
padding = 39 - len(d) + len(status) + (len(status_ok) - len(status)) padding = 39 - len(d) + len(status) + (len(status_ok) - len(status))
d = f" {COLOR_CYAN}{d}{RESET_FORMAT}" d = Color.apply(f" {d}", Color.CYAN)
menu += f"{d}{status:>{padding}}\n" menu += f"{d}{status:>{padding}}\n"
menu += "║ ║\n" menu += "║ ║\n"
if len(self.missing_deps) == 0: color = Color.GREEN if len(self.missing_deps) == 0 else Color.RED
line = f"{COLOR_GREEN}All dependencies are met!{RESET_FORMAT}" txt = (
else: "All dependencies are met!"
line = f"{COLOR_RED}Dependencies are missing!{RESET_FORMAT}" if len(self.missing_deps) == 0
else "Dependencies are missing!"
)
menu += f"{line:<62}\n" menu += f"{Color.apply(txt, color):<62}\n"
menu += "╟───────────────────────────────────────────────────────╢\n" menu += "╟───────────────────────────────────────────────────────╢\n"
print(menu, end="") print(menu, end="")

View File

@@ -12,9 +12,9 @@ import textwrap
from typing import Type from typing import Type
from components.klipper_firmware.flash_options import FlashMethod, FlashOptions from components.klipper_firmware.flash_options import FlashMethod, FlashOptions
from core.constants import COLOR_RED, RESET_FORMAT
from core.menus import FooterType, Option from core.menus import FooterType, Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu, MenuTitleStyle
from core.types.color import Color
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
@@ -22,6 +22,9 @@ from core.menus.base_menu import BaseMenu
class KlipperNoFirmwareErrorMenu(BaseMenu): class KlipperNoFirmwareErrorMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "!!! NO FIRMWARE FILE FOUND !!!"
self.title_color = Color.RED
self.title_style = MenuTitleStyle.PLAIN
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.flash_options = FlashOptions() self.flash_options = FlashOptions()
@@ -35,16 +38,11 @@ class KlipperNoFirmwareErrorMenu(BaseMenu):
self.default_option = Option(method=self.go_back) self.default_option = Option(method=self.go_back)
def print_menu(self) -> None: def print_menu(self) -> None:
header = "!!! NO FIRMWARE FILE FOUND !!!" line1 = "Unable to find a compiled firmware file!"
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
line1 = f"{color}Unable to find a compiled firmware file!{RESET_FORMAT}"
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
{line1:<62} {Color.apply(line1, Color.RED):<62}
║ ║ ║ ║
║ Make sure, that: ║ ║ Make sure, that: ║
║ ● the folder '~/klipper/out' and its content exist ║ ║ ● the folder '~/klipper/out' and its content exist ║
@@ -71,6 +69,9 @@ class KlipperNoFirmwareErrorMenu(BaseMenu):
class KlipperNoBoardTypesErrorMenu(BaseMenu): class KlipperNoBoardTypesErrorMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "!!! ERROR GETTING BOARD LIST !!!"
self.title_color = Color.RED
self.title_style = MenuTitleStyle.PLAIN
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.footer_type = FooterType.BLANK self.footer_type = FooterType.BLANK
self.input_label_txt = "Press ENTER to go back to [Main Menu]" self.input_label_txt = "Press ENTER to go back to [Main Menu]"
@@ -82,16 +83,11 @@ class KlipperNoBoardTypesErrorMenu(BaseMenu):
self.default_option = Option(method=self.go_back) self.default_option = Option(method=self.go_back)
def print_menu(self) -> None: def print_menu(self) -> None:
header = "!!! ERROR GETTING BOARD LIST !!!" line1 = "Reading the list of supported boards failed!"
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
line1 = f"{color}Reading the list of supported boards failed!{RESET_FORMAT}"
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
{line1:<62} {Color.apply(line1, Color.RED):<62}
║ ║ ║ ║
║ Make sure, that: ║ ║ Make sure, that: ║
║ ● the folder '~/klipper' and all its content exist ║ ║ ● the folder '~/klipper' and all its content exist ║

View File

@@ -9,16 +9,21 @@
from __future__ import annotations from __future__ import annotations
import textwrap import textwrap
from typing import Type from typing import Tuple, Type
from core.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT from core.menus.base_menu import BaseMenu, MenuTitleStyle
from core.menus.base_menu import BaseMenu from core.types.color import Color
def __title_config__() -> Tuple[str, Color, MenuTitleStyle]:
return "< ? > Help: Flash MCU < ? >", Color.YELLOW, MenuTitleStyle.PLAIN
# noinspection DuplicatedCode # noinspection DuplicatedCode
class KlipperFlashMethodHelpMenu(BaseMenu): class KlipperFlashMethodHelpMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title, self.title_color, self.title_style = __title_config__()
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
@@ -34,15 +39,10 @@ class KlipperFlashMethodHelpMenu(BaseMenu):
pass pass
def print_menu(self) -> None: def print_menu(self) -> None:
header = " < ? > Help: Flash MCU < ? > " subheader1 = Color.apply("Regular flashing method:", Color.CYAN)
color = COLOR_YELLOW subheader2 = Color.apply("Updating via SD-Card Update:", Color.CYAN)
count = 62 - len(color) - len(RESET_FORMAT)
subheader1 = f"{COLOR_CYAN}Regular flashing method:{RESET_FORMAT}"
subheader2 = f"{COLOR_CYAN}Updating via SD-Card Update:{RESET_FORMAT}"
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
{subheader1:<62} {subheader1:<62}
║ The default method to flash controller boards which ║ ║ The default method to flash controller boards which ║
@@ -77,6 +77,7 @@ class KlipperFlashMethodHelpMenu(BaseMenu):
class KlipperFlashCommandHelpMenu(BaseMenu): class KlipperFlashCommandHelpMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title, self.title_color, self.title_style = __title_config__()
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
@@ -92,15 +93,10 @@ class KlipperFlashCommandHelpMenu(BaseMenu):
pass pass
def print_menu(self) -> None: def print_menu(self) -> None:
header = " < ? > Help: Flash MCU < ? > " subheader1 = Color.apply("make flash:", Color.CYAN)
color = COLOR_YELLOW subheader2 = Color.apply("make serialflash:", Color.CYAN)
count = 62 - len(color) - len(RESET_FORMAT)
subheader1 = f"{COLOR_CYAN}make flash:{RESET_FORMAT}"
subheader2 = f"{COLOR_CYAN}make serialflash:{RESET_FORMAT}"
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
{subheader1:<62} {subheader1:<62}
║ The default command to flash controller board, it ║ ║ The default command to flash controller board, it ║
@@ -121,6 +117,7 @@ class KlipperFlashCommandHelpMenu(BaseMenu):
class KlipperMcuConnectionHelpMenu(BaseMenu): class KlipperMcuConnectionHelpMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title, self.title_color, self.title_style = __title_config__()
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
@@ -138,17 +135,12 @@ class KlipperMcuConnectionHelpMenu(BaseMenu):
pass pass
def print_menu(self) -> None: def print_menu(self) -> None:
header = " < ? > Help: Flash MCU < ? > " subheader1 = Color.apply("USB:", Color.CYAN)
color = COLOR_YELLOW subheader2 = Color.apply("UART:", Color.CYAN)
count = 62 - len(color) - len(RESET_FORMAT) subheader3 = Color.apply("USB DFU:", Color.CYAN)
subheader1 = f"{COLOR_CYAN}USB:{RESET_FORMAT}" subheader4 = Color.apply("USB RP2040 Boot:", Color.CYAN)
subheader2 = f"{COLOR_CYAN}UART:{RESET_FORMAT}"
subheader3 = f"{COLOR_CYAN}USB DFU:{RESET_FORMAT}"
subheader4 = f"{COLOR_CYAN}USB RP2040 Boot:{RESET_FORMAT}"
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
{subheader1:<62} {subheader1:<62}
║ Selecting USB as the connection method will scan the ║ ║ Selecting USB as the connection method will scan the ║

View File

@@ -36,10 +36,10 @@ from components.klipper_firmware.menus.klipper_flash_help_menu import (
KlipperFlashMethodHelpMenu, KlipperFlashMethodHelpMenu,
KlipperMcuConnectionHelpMenu, KlipperMcuConnectionHelpMenu,
) )
from core.constants import COLOR_CYAN, COLOR_RED, COLOR_YELLOW, RESET_FORMAT
from core.logger import DialogType, Logger from core.logger import DialogType, Logger
from core.menus import FooterType, Option from core.menus import FooterType, Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu, MenuTitleStyle
from core.types.color import Color
from utils.input_utils import get_number_input from utils.input_utils import get_number_input
@@ -48,6 +48,8 @@ from utils.input_utils import get_number_input
class KlipperFlashMethodMenu(BaseMenu): class KlipperFlashMethodMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "MCU Flash Menu"
self.title_color = Color.CYAN
self.help_menu = KlipperFlashMethodHelpMenu self.help_menu = KlipperFlashMethodHelpMenu
self.input_label_txt = "Select flash method" self.input_label_txt = "Select flash method"
self.footer_type = FooterType.BACK_HELP self.footer_type = FooterType.BACK_HELP
@@ -67,17 +69,13 @@ class KlipperFlashMethodMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ MCU Flash Menu ] " subheader = Color.apply("ATTENTION:", Color.YELLOW)
subheader = f"{COLOR_YELLOW}ATTENTION:{RESET_FORMAT}" subline1 = Color.apply(
subline1 = f"{COLOR_YELLOW}Make sure to select the correct method for the MCU!{RESET_FORMAT}" "Make sure to select the correct method for the MCU!", Color.YELLOW
subline2 = f"{COLOR_YELLOW}Not all MCUs support both methods!{RESET_FORMAT}" )
subline2 = Color.apply("Not all MCUs support both methods!", Color.YELLOW)
color = COLOR_CYAN
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ Select the flash method for flashing the MCU. ║ ║ Select the flash method for flashing the MCU. ║
║ ║ ║ ║
@@ -112,6 +110,9 @@ class KlipperFlashMethodMenu(BaseMenu):
class KlipperFlashCommandMenu(BaseMenu): class KlipperFlashCommandMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "Which flash command to use for flashing the MCU?"
self.title_style = MenuTitleStyle.PLAIN
self.title_color = Color.YELLOW
self.help_menu = KlipperFlashCommandHelpMenu self.help_menu = KlipperFlashCommandHelpMenu
self.input_label_txt = "Select flash command" self.input_label_txt = "Select flash command"
self.footer_type = FooterType.BACK_HELP self.footer_type = FooterType.BACK_HELP
@@ -132,8 +133,6 @@ class KlipperFlashCommandMenu(BaseMenu):
def print_menu(self) -> None: def print_menu(self) -> None:
menu = textwrap.dedent( menu = textwrap.dedent(
""" """
╔═══════════════════════════════════════════════════════╗
║ Which flash command to use for flashing the MCU? ║
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ 1) make flash (default) ║ ║ 1) make flash (default) ║
║ 2) make serialflash (stm32flash) ║ ║ 2) make serialflash (stm32flash) ║
@@ -161,6 +160,9 @@ class KlipperSelectMcuConnectionMenu(BaseMenu):
self, previous_menu: Type[BaseMenu] | None = None, standalone: bool = False self, previous_menu: Type[BaseMenu] | None = None, standalone: bool = False
): ):
super().__init__() super().__init__()
self.title = "Make sure that the controller board is connected now!"
self.title_style = MenuTitleStyle.PLAIN
self.title_color = Color.YELLOW
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.__standalone = standalone self.__standalone = standalone
self.help_menu = KlipperMcuConnectionHelpMenu self.help_menu = KlipperMcuConnectionHelpMenu
@@ -182,13 +184,8 @@ class KlipperSelectMcuConnectionMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = "Make sure that the controller board is connected now!"
color = COLOR_YELLOW
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent( menu = textwrap.dedent(
f""" """
╔═══════════════════════════════════════════════════════╗
{color}{header:^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ How is the controller board connected to the host? ║ ║ How is the controller board connected to the host? ║
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
@@ -230,7 +227,9 @@ class KlipperSelectMcuConnectionMenu(BaseMenu):
Logger.print_status("Identifying MCU connected via USB in DFU mode ...") Logger.print_status("Identifying MCU connected via USB in DFU mode ...")
self.flash_options.mcu_list = find_usb_dfu_device() self.flash_options.mcu_list = find_usb_dfu_device()
elif conn_type is ConnectionType.USB_RP2040: elif conn_type is ConnectionType.USB_RP2040:
Logger.print_status("Identifying MCU connected via USB in RP2 Boot mode ...") Logger.print_status(
"Identifying MCU connected via USB in RP2 Boot mode ..."
)
self.flash_options.mcu_list = find_usb_rp2_boot_device() self.flash_options.mcu_list = find_usb_rp2_boot_device()
if len(self.flash_options.mcu_list) < 1: if len(self.flash_options.mcu_list) < 1:
@@ -241,7 +240,7 @@ class KlipperSelectMcuConnectionMenu(BaseMenu):
if self.__standalone and len(self.flash_options.mcu_list) > 0: if self.__standalone and len(self.flash_options.mcu_list) > 0:
Logger.print_ok("The following MCUs were found:", prefix=False) Logger.print_ok("The following MCUs were found:", prefix=False)
for i, mcu in enumerate(self.flash_options.mcu_list): for i, mcu in enumerate(self.flash_options.mcu_list):
print(f" ● MCU #{i}: {COLOR_CYAN}{mcu}{RESET_FORMAT}") print(f" ● MCU #{i}: {Color.CYAN}{mcu}{Color.RST}")
time.sleep(3) time.sleep(3)
return return
@@ -256,6 +255,9 @@ class KlipperSelectMcuConnectionMenu(BaseMenu):
class KlipperSelectMcuIdMenu(BaseMenu): class KlipperSelectMcuIdMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "!!! ATTENTION !!!"
self.title_style = MenuTitleStyle.PLAIN
self.title_color = Color.RED
self.flash_options = FlashOptions() self.flash_options = FlashOptions()
self.mcu_list = self.flash_options.mcu_list self.mcu_list = self.flash_options.mcu_list
self.input_label_txt = "Select MCU to flash" self.input_label_txt = "Select MCU to flash"
@@ -274,14 +276,9 @@ class KlipperSelectMcuIdMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = "!!! ATTENTION !!!" header2 = f"[{Color.apply('List of detected MCUs', Color.CYAN)}]"
header2 = f"[{COLOR_CYAN}List of detected MCUs{RESET_FORMAT}]"
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ Make sure, to select the correct MCU! ║ ║ Make sure, to select the correct MCU! ║
║ ONLY flash a firmware created for the respective MCU! ║ ║ ONLY flash a firmware created for the respective MCU! ║
@@ -293,7 +290,7 @@ class KlipperSelectMcuIdMenu(BaseMenu):
for i, mcu in enumerate(self.mcu_list): for i, mcu in enumerate(self.mcu_list):
mcu = mcu.split("/")[-1] mcu = mcu.split("/")[-1]
menu += f"{i}) {COLOR_CYAN}{mcu:<51}{RESET_FORMAT}\n" menu += f"{i}) {Color.apply(f'{mcu:<51}', Color.CYAN)}\n"
menu += textwrap.dedent( menu += textwrap.dedent(
""" """
@@ -348,7 +345,6 @@ class KlipperSelectSDFlashBoardMenu(BaseMenu):
else: else:
menu = textwrap.dedent( menu = textwrap.dedent(
""" """
╔═══════════════════════════════════════════════════════╗
║ Please select the type of board that corresponds to ║ ║ Please select the type of board that corresponds to ║
║ the currently selected MCU ID you chose before. ║ ║ the currently selected MCU ID you chose before. ║
║ ║ ║ ║
@@ -400,6 +396,9 @@ class KlipperSelectSDFlashBoardMenu(BaseMenu):
class KlipperFlashOverviewMenu(BaseMenu): class KlipperFlashOverviewMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "!!! ATTENTION !!!"
self.title_style = MenuTitleStyle.PLAIN
self.title_color = Color.RED
self.flash_options = FlashOptions() self.flash_options = FlashOptions()
self.input_label_txt = "Perform action (default=Y)" self.input_label_txt = "Perform action (default=Y)"
@@ -415,21 +414,16 @@ class KlipperFlashOverviewMenu(BaseMenu):
self.default_option = Option(self.execute_flash) self.default_option = Option(self.execute_flash)
def print_menu(self) -> None: def print_menu(self) -> None:
header = "!!! ATTENTION !!!"
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
method = self.flash_options.flash_method.value method = self.flash_options.flash_method.value
command = self.flash_options.flash_command.value command = self.flash_options.flash_command.value
conn_type = self.flash_options.connection_type.value conn_type = self.flash_options.connection_type.value
mcu = self.flash_options.selected_mcu.split("/")[-1] mcu = self.flash_options.selected_mcu.split("/")[-1]
board = self.flash_options.selected_board board = self.flash_options.selected_board
baudrate = self.flash_options.selected_baudrate baudrate = self.flash_options.selected_baudrate
subheader = f"[{COLOR_CYAN}Overview{RESET_FORMAT}]" color = Color.CYAN
subheader = f"[{Color.apply('Overview', color)}]"
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ Before contuining the flashing process, please check ║ ║ Before contuining the flashing process, please check ║
║ if all parameters were set correctly! Once you made ║ ║ if all parameters were set correctly! Once you made ║
@@ -443,18 +437,18 @@ class KlipperFlashOverviewMenu(BaseMenu):
menu += textwrap.dedent( menu += textwrap.dedent(
f""" f"""
║ MCU: {COLOR_CYAN}{mcu:<48}{RESET_FORMAT} ║ MCU: {Color.apply(f"{mcu:<48}", color)}
║ Connection: {COLOR_CYAN}{conn_type:<41}{RESET_FORMAT} ║ Connection: {Color.apply(f"{conn_type:<41}", color)}
║ Flash method: {COLOR_CYAN}{method:<39}{RESET_FORMAT} ║ Flash method: {Color.apply(f"{method:<39}", color)}
║ Flash command: {COLOR_CYAN}{command:<38}{RESET_FORMAT} ║ Flash command: {Color.apply(f"{command:<38}", color)}
""" """
)[1:] )[1:]
if self.flash_options.flash_method is FlashMethod.SD_CARD: if self.flash_options.flash_method is FlashMethod.SD_CARD:
menu += textwrap.dedent( menu += textwrap.dedent(
f""" f"""
║ Board type: {COLOR_CYAN}{board:<41}{RESET_FORMAT} ║ Board type: {Color.apply(f"{board:<41}", color)}
║ Baudrate: {COLOR_CYAN}{baudrate:<43}{RESET_FORMAT} ║ Baudrate: {Color.apply(f"{baudrate:<43}", color)}
""" """
)[1:] )[1:]

View File

@@ -30,7 +30,7 @@ from core.constants import SYSTEMD
from core.instance_manager.instance_manager import InstanceManager from core.instance_manager.instance_manager import InstanceManager
from core.logger import DialogType, Logger from core.logger import DialogType, Logger
from core.settings.kiauh_settings import KiauhSettings from core.settings.kiauh_settings import KiauhSettings
from core.types import ComponentStatus from core.types.component_status import ComponentStatus
from utils.common import ( from utils.common import (
check_install_dependencies, check_install_dependencies,
get_install_status, get_install_status,

View File

@@ -12,16 +12,18 @@ import textwrap
from typing import Type from typing import Type
from components.log_uploads.log_upload_utils import get_logfile_list, upload_logfile from components.log_uploads.log_upload_utils import get_logfile_list, upload_logfile
from core.constants import COLOR_YELLOW, RESET_FORMAT
from core.logger import Logger from core.logger import Logger
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.types.color import Color
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class LogUploadMenu(BaseMenu): class LogUploadMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "Log Upload"
self.title_color = Color.YELLOW
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.logfile_list = get_logfile_list() self.logfile_list = get_logfile_list()
@@ -37,13 +39,8 @@ class LogUploadMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ Log Upload ] "
color = COLOR_YELLOW
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent( menu = textwrap.dedent(
f""" """
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ You can select the following logfiles for uploading: ║ ║ You can select the following logfiles for uploading: ║
║ ║ ║ ║

View File

@@ -12,15 +12,17 @@ import textwrap
from typing import Type from typing import Type
from components.moonraker import moonraker_remove from components.moonraker import moonraker_remove
from core.constants import COLOR_CYAN, COLOR_RED, RESET_FORMAT
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.types.color import Color
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
class MoonrakerRemoveMenu(BaseMenu): class MoonrakerRemoveMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "Remove Moonraker"
self.title_color = Color.RED
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.remove_moonraker_service = False self.remove_moonraker_service = False
self.remove_moonraker_dir = False self.remove_moonraker_dir = False
@@ -44,10 +46,7 @@ class MoonrakerRemoveMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ Remove Moonraker ] " checked = f"[{Color.apply('x', Color.CYAN)}]"
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
checked = f"[{COLOR_CYAN}x{RESET_FORMAT}]"
unchecked = "[ ]" unchecked = "[ ]"
o1 = checked if self.remove_moonraker_service else unchecked o1 = checked if self.remove_moonraker_service else unchecked
o2 = checked if self.remove_moonraker_dir else unchecked o2 = checked if self.remove_moonraker_dir else unchecked
@@ -55,8 +54,6 @@ class MoonrakerRemoveMenu(BaseMenu):
o4 = checked if self.remove_moonraker_polkit else unchecked o4 = checked if self.remove_moonraker_polkit else unchecked
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ Enter a number and hit enter to select / deselect ║ ║ Enter a number and hit enter to select / deselect ║
║ the specific option for removal. ║ ║ the specific option for removal. ║
@@ -100,8 +97,11 @@ class MoonrakerRemoveMenu(BaseMenu):
and not self.remove_moonraker_env and not self.remove_moonraker_env
and not self.remove_moonraker_polkit and not self.remove_moonraker_polkit
): ):
error = f"{COLOR_RED}Nothing selected! Select options to remove first.{RESET_FORMAT}" print(
print(error) Color.apply(
"Nothing selected! Select options to remove first.", Color.RED
)
)
return return
moonraker_remove.run_moonraker_removal( moonraker_remove.run_moonraker_removal(

View File

@@ -12,8 +12,8 @@ from typing import List
from components.klipper.klipper import Klipper from components.klipper.klipper import Klipper
from components.moonraker.moonraker import Moonraker from components.moonraker.moonraker import Moonraker
from core.constants import COLOR_CYAN, COLOR_GREEN, COLOR_YELLOW, RESET_FORMAT
from core.menus.base_menu import print_back_footer from core.menus.base_menu import print_back_footer
from core.types.color import Color
def print_moonraker_overview( def print_moonraker_overview(
@@ -22,7 +22,7 @@ def print_moonraker_overview(
show_index=False, show_index=False,
show_select_all=False, show_select_all=False,
): ):
headline = f"{COLOR_GREEN}The following instances were found:{RESET_FORMAT}" headline = Color.apply("The following instances were found:", Color.GREEN)
dialog = textwrap.dedent( dialog = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗ ╔═══════════════════════════════════════════════════════╗
@@ -32,7 +32,7 @@ def print_moonraker_overview(
)[1:] )[1:]
if show_select_all: if show_select_all:
select_all = f"{COLOR_YELLOW}a) Select all{RESET_FORMAT}" select_all = Color.apply("a) Select all", Color.YELLOW)
dialog += f"{select_all:<63}\n" dialog += f"{select_all:<63}\n"
dialog += "║ ║\n" dialog += "║ ║\n"
@@ -48,12 +48,16 @@ def print_moonraker_overview(
for i, k in enumerate(instance_map): for i, k in enumerate(instance_map):
mr_name = instance_map.get(k) mr_name = instance_map.get(k)
m = f"<-> {mr_name}" if mr_name != "" else "" m = f"<-> {mr_name}" if mr_name != "" else ""
line = f"{COLOR_CYAN}{f'{i+1})' if show_index else ''} {k} {m} {RESET_FORMAT}" line = Color.apply(f"{f'{i+1})' if show_index else ''} {k} {m}", Color.CYAN)
dialog += f"{line:<63}\n" dialog += f"{line:<63}\n"
warn_l1 = f"{COLOR_YELLOW}PLEASE NOTE: {RESET_FORMAT}" warn_l1 = Color.apply("PLEASE NOTE:", Color.YELLOW)
warn_l2 = f"{COLOR_YELLOW}If you select an instance with an existing Moonraker{RESET_FORMAT}" warn_l2 = Color.apply(
warn_l3 = f"{COLOR_YELLOW}instance, that Moonraker instance will be re-created!{RESET_FORMAT}" "If you select an instance with an existing Moonraker", Color.YELLOW
)
warn_l3 = Color.apply(
"instance, that Moonraker instance will be re-created!", Color.YELLOW
)
warning = textwrap.dedent( warning = textwrap.dedent(
f""" f"""
║ ║ ║ ║

View File

@@ -25,7 +25,7 @@ from core.logger import Logger
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import ( from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
SimpleConfigParser, SimpleConfigParser,
) )
from core.types import ComponentStatus from core.types.component_status import ComponentStatus
from utils.common import get_install_status from utils.common import get_install_status
from utils.instance_utils import get_instances from utils.instance_utils import get_instances
from utils.sys_utils import ( from utils.sys_utils import (

View File

@@ -36,8 +36,9 @@ from components.webui_client.client_utils import (
symlink_webui_nginx_log, symlink_webui_nginx_log,
) )
from core.instance_manager.instance_manager import InstanceManager from core.instance_manager.instance_manager import InstanceManager
from core.logger import DialogCustomColor, DialogType, Logger from core.logger import DialogType, Logger
from core.settings.kiauh_settings import KiauhSettings from core.settings.kiauh_settings import KiauhSettings
from core.types.color import Color
from utils.common import backup_printer_config_dir, check_install_dependencies from utils.common import backup_printer_config_dir, check_install_dependencies
from utils.config_utils import add_config_section from utils.config_utils import add_config_section
from utils.fs_utils import unzip from utils.fs_utils import unzip
@@ -140,7 +141,7 @@ def install_client(
Logger.print_dialog( Logger.print_dialog(
DialogType.CUSTOM, DialogType.CUSTOM,
custom_title=f"{client.display_name} installation complete!", custom_title=f"{client.display_name} installation complete!",
custom_color=DialogCustomColor.GREEN, custom_color=Color.GREEN,
center_content=True, center_content=True,
content=[ content=[
f"Open {client.display_name} now on: http://{get_ipv4_addr()}:{port}", f"Open {client.display_name} now on: http://{get_ipv4_addr()}:{port}",

View File

@@ -26,19 +26,17 @@ from components.webui_client.fluidd_data import FluiddData
from components.webui_client.mainsail_data import MainsailData from components.webui_client.mainsail_data import MainsailData
from core.backup_manager.backup_manager import BackupManager from core.backup_manager.backup_manager import BackupManager
from core.constants import ( from core.constants import (
COLOR_CYAN,
COLOR_YELLOW,
NGINX_CONFD, NGINX_CONFD,
NGINX_SITES_AVAILABLE, NGINX_SITES_AVAILABLE,
NGINX_SITES_ENABLED, NGINX_SITES_ENABLED,
RESET_FORMAT,
) )
from core.logger import Logger from core.logger import Logger
from core.settings.kiauh_settings import KiauhSettings, WebUiSettings from core.settings.kiauh_settings import KiauhSettings, WebUiSettings
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import ( from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
SimpleConfigParser, SimpleConfigParser,
) )
from core.types import ComponentStatus from core.types.color import Color
from core.types.component_status import ComponentStatus
from utils.common import get_install_status from utils.common import get_install_status
from utils.fs_utils import create_symlink, remove_file from utils.fs_utils import create_symlink, remove_file
from utils.git_utils import ( from utils.git_utils import (
@@ -79,10 +77,10 @@ def get_current_client_config() -> str:
installed = [c for c in clients if c.client_config.config_dir.exists()] installed = [c for c in clients if c.client_config.config_dir.exists()]
if not installed: if not installed:
return f"{COLOR_CYAN}-{RESET_FORMAT}" return Color.apply("-", Color.CYAN)
elif len(installed) == 1: elif len(installed) == 1:
cfg = installed[0].client_config cfg = installed[0].client_config
return f"{COLOR_CYAN}{cfg.display_name}{RESET_FORMAT}" return Color.apply(cfg.display_name, Color.CYAN)
# at this point, both client config folders exists, so we need to check # at this point, both client config folders exists, so we need to check
# which are actually included in the printer.cfg of all klipper instances # which are actually included in the printer.cfg of all klipper instances
@@ -101,18 +99,18 @@ def get_current_client_config() -> str:
# if both are included in the same file, we have a potential conflict # if both are included in the same file, we have a potential conflict
if includes_mainsail and includes_fluidd: if includes_mainsail and includes_fluidd:
return f"{COLOR_YELLOW}Conflict!{RESET_FORMAT}" return Color.apply("Conflict", Color.YELLOW)
if not mainsail_includes and not fluidd_includes: if not mainsail_includes and not fluidd_includes:
# there are no includes at all, even though the client config folders exist # there are no includes at all, even though the client config folders exist
return f"{COLOR_CYAN}-{RESET_FORMAT}" return Color.apply("-", Color.CYAN)
elif len(fluidd_includes) > len(mainsail_includes): elif len(fluidd_includes) > len(mainsail_includes):
# there are more instances that include fluidd than mainsail # there are more instances that include fluidd than mainsail
return f"{COLOR_CYAN}{fluidd.client_config.display_name}{RESET_FORMAT}" return Color.apply(fluidd.client_config.display_name, Color.CYAN)
else: else:
# there are the same amount of non-conflicting includes for each config # there are the same amount of non-conflicting includes for each config
# or more instances include mainsail than fluidd # or more instances include mainsail than fluidd
return f"{COLOR_CYAN}{mainsail.client_config.display_name}{RESET_FORMAT}" return Color.apply(mainsail.client_config.display_name, Color.CYAN)
def enable_mainsail_remotemode() -> None: def enable_mainsail_remotemode() -> None:

View File

@@ -18,11 +18,11 @@ from components.webui_client.client_utils import (
get_nginx_listen_port, get_nginx_listen_port,
set_listen_port, set_listen_port,
) )
from core.constants import COLOR_CYAN, COLOR_GREEN, RESET_FORMAT from core.logger import DialogType, Logger
from core.logger import DialogCustomColor, DialogType, Logger
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.settings.kiauh_settings import KiauhSettings, WebUiSettings from core.settings.kiauh_settings import KiauhSettings, WebUiSettings
from core.types.color import Color
from utils.sys_utils import cmd_sysctl_service, get_ipv4_addr from utils.sys_utils import cmd_sysctl_service, get_ipv4_addr
@@ -32,6 +32,8 @@ class ClientInstallMenu(BaseMenu):
self, client: BaseWebClient, previous_menu: Type[BaseMenu] | None = None self, client: BaseWebClient, previous_menu: Type[BaseMenu] | None = None
): ):
super().__init__() super().__init__()
self.title = f"Installation Menu > {client.display_name}"
self.title_color = Color.GREEN
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.client: BaseWebClient = client self.client: BaseWebClient = client
self.settings = KiauhSettings() self.settings = KiauhSettings()
@@ -50,15 +52,9 @@ class ClientInstallMenu(BaseMenu):
def print_menu(self) -> None: def print_menu(self) -> None:
client_name = self.client.display_name client_name = self.client.display_name
port = f"(Current: {Color.apply(self._get_current_port(), Color.GREEN)})"
header = f" [ Installation Menu > {client_name} ] "
color = COLOR_GREEN
count = 62 - len(color) - len(RESET_FORMAT)
port = f"(Current: {COLOR_CYAN}{self._get_current_port()}{RESET_FORMAT})"
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ 1) Reinstall {client_name:16} ║ 1) Reinstall {client_name:16}
║ 2) Reconfigure Listen Port {port:<34} ║ 2) Reconfigure Listen Port {port:<34}
@@ -92,7 +88,7 @@ class ClientInstallMenu(BaseMenu):
Logger.print_dialog( Logger.print_dialog(
DialogType.CUSTOM, DialogType.CUSTOM,
custom_title="Port reconfiguration complete!", custom_title="Port reconfiguration complete!",
custom_color=DialogCustomColor.GREEN, custom_color=Color.GREEN,
center_content=True, center_content=True,
content=[ content=[
f"Open {self.client.display_name} now on: " f"Open {self.client.display_name} now on: "

View File

@@ -13,9 +13,9 @@ from typing import Type
from components.webui_client import client_remove from components.webui_client import client_remove
from components.webui_client.base_data import BaseWebClient from components.webui_client.base_data import BaseWebClient
from core.constants import COLOR_CYAN, COLOR_RED, RESET_FORMAT
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.types.color import Color
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
@@ -24,6 +24,8 @@ class ClientRemoveMenu(BaseMenu):
self, client: BaseWebClient, previous_menu: Type[BaseMenu] | None = None self, client: BaseWebClient, previous_menu: Type[BaseMenu] | None = None
): ):
super().__init__() super().__init__()
self.title = f"Remove {client.display_name}"
self.title_color = Color.RED
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.client: BaseWebClient = client self.client: BaseWebClient = client
self.remove_client: bool = False self.remove_client: bool = False
@@ -50,18 +52,13 @@ class ClientRemoveMenu(BaseMenu):
client_config = self.client.client_config client_config = self.client.client_config
client_config_name = client_config.display_name client_config_name = client_config.display_name
header = f" [ Remove {client_name} ] " checked = f"[{Color.apply('x', Color.CYAN)}]"
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
checked = f"[{COLOR_CYAN}x{RESET_FORMAT}]"
unchecked = "[ ]" unchecked = "[ ]"
o1 = checked if self.remove_client else unchecked o1 = checked if self.remove_client else unchecked
o2 = checked if self.remove_client_cfg else unchecked o2 = checked if self.remove_client_cfg else unchecked
o3 = checked if self.backup_config_json else unchecked o3 = checked if self.backup_config_json else unchecked
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ Enter a number and hit enter to select / deselect ║ ║ Enter a number and hit enter to select / deselect ║
║ the specific option for removal. ║ ║ the specific option for removal. ║
@@ -99,8 +96,7 @@ class ClientRemoveMenu(BaseMenu):
and not self.remove_client_cfg and not self.remove_client_cfg
and not self.backup_config_json and not self.backup_config_json
): ):
error = f"{COLOR_RED}Nothing selected ...{RESET_FORMAT}" print(Color.apply("Nothing selected ...", Color.RED))
print(error)
return return
client_remove.run_client_removal( client_remove.run_client_removal(

View File

@@ -13,15 +13,6 @@ from pathlib import Path
from core.backup_manager import BACKUP_ROOT_DIR from core.backup_manager import BACKUP_ROOT_DIR
# text colors and formats
COLOR_WHITE = "\033[37m" # white
COLOR_MAGENTA = "\033[35m" # magenta
COLOR_GREEN = "\033[92m" # bright green
COLOR_YELLOW = "\033[93m" # bright yellow
COLOR_RED = "\033[91m" # bright red
COLOR_CYAN = "\033[96m" # bright cyan
RESET_FORMAT = "\033[0m" # reset format
# global dependencies # global dependencies
GLOBAL_DEPS = ["git", "wget", "curl", "unzip", "dfu-util", "python3-virtualenv"] GLOBAL_DEPS = ["git", "wget", "curl", "unzip", "dfu-util", "python3-virtualenv"]

View File

@@ -12,78 +12,50 @@ import textwrap
from enum import Enum from enum import Enum
from typing import List from typing import List
from core.constants import ( from core.types.color import Color
COLOR_CYAN,
COLOR_GREEN,
COLOR_MAGENTA,
COLOR_RED,
COLOR_WHITE,
COLOR_YELLOW,
RESET_FORMAT,
)
class DialogType(Enum): class DialogType(Enum):
INFO = ("INFO", COLOR_WHITE) INFO = ("INFO", Color.WHITE)
SUCCESS = ("SUCCESS", COLOR_GREEN) SUCCESS = ("SUCCESS", Color.GREEN)
ATTENTION = ("ATTENTION", COLOR_YELLOW) ATTENTION = ("ATTENTION", Color.YELLOW)
WARNING = ("WARNING", COLOR_YELLOW) WARNING = ("WARNING", Color.YELLOW)
ERROR = ("ERROR", COLOR_RED) ERROR = ("ERROR", Color.RED)
CUSTOM = (None, None) CUSTOM = (None, None)
class DialogCustomColor(Enum):
WHITE = COLOR_WHITE
GREEN = COLOR_GREEN
YELLOW = COLOR_YELLOW
RED = COLOR_RED
CYAN = COLOR_CYAN
MAGENTA = COLOR_MAGENTA
LINE_WIDTH = 53 LINE_WIDTH = 53
class Logger: class Logger:
@staticmethod
def info(msg) -> None:
# log to kiauh.log
pass
@staticmethod
def warn(msg) -> None:
# log to kiauh.log
pass
@staticmethod
def error(msg) -> None:
# log to kiauh.log
pass
@staticmethod @staticmethod
def print_info(msg, prefix=True, start="", end="\n") -> None: def print_info(msg, prefix=True, start="", end="\n") -> None:
message = f"[INFO] {msg}" if prefix else msg message = f"[INFO] {msg}" if prefix else msg
print(f"{COLOR_WHITE}{start}{message}{RESET_FORMAT}", end=end) Logger.__print(Color.WHITE, start, message, end)
@staticmethod @staticmethod
def print_ok(msg: str = "Success!", prefix=True, start="", end="\n") -> None: def print_ok(msg: str = "Success!", prefix=True, start="", end="\n") -> None:
message = f"[OK] {msg}" if prefix else msg message = f"[OK] {msg}" if prefix else msg
print(f"{COLOR_GREEN}{start}{message}{RESET_FORMAT}", end=end) Logger.__print(Color.GREEN, start, message, end)
@staticmethod @staticmethod
def print_warn(msg, prefix=True, start="", end="\n") -> None: def print_warn(msg, prefix=True, start="", end="\n") -> None:
message = f"[WARN] {msg}" if prefix else msg message = f"[WARN] {msg}" if prefix else msg
print(f"{COLOR_YELLOW}{start}{message}{RESET_FORMAT}", end=end) Logger.__print(Color.YELLOW, start, message, end)
@staticmethod @staticmethod
def print_error(msg, prefix=True, start="", end="\n") -> None: def print_error(msg, prefix=True, start="", end="\n") -> None:
message = f"[ERROR] {msg}" if prefix else msg message = f"[ERROR] {msg}" if prefix else msg
print(f"{COLOR_RED}{start}{message}{RESET_FORMAT}", end=end) Logger.__print(Color.RED, start, message, end)
@staticmethod @staticmethod
def print_status(msg, prefix=True, start="", end="\n") -> None: def print_status(msg, prefix=True, start="", end="\n") -> None:
message = f"\n###### {msg}" if prefix else msg message = f"\n###### {msg}" if prefix else msg
print(f"{COLOR_MAGENTA}{start}{message}{RESET_FORMAT}", end=end) Logger.__print(Color.MAGENTA, start, message, end)
@staticmethod
def __print(color: Color, start: str, message: str, end: str) -> None:
print(Color.apply(f"{start}{message}", color), end=end)
@staticmethod @staticmethod
def print_dialog( def print_dialog(
@@ -91,7 +63,7 @@ class Logger:
content: List[str], content: List[str],
center_content: bool = False, center_content: bool = False,
custom_title: str | None = None, custom_title: str | None = None,
custom_color: DialogCustomColor | None = None, custom_color: Color | None = None,
margin_top: int = 0, margin_top: int = 0,
margin_bottom: int = 0, margin_bottom: int = 0,
) -> None: ) -> None:
@@ -111,10 +83,15 @@ class Logger:
""" """
dialog_color = Logger._get_dialog_color(title, custom_color) dialog_color = Logger._get_dialog_color(title, custom_color)
dialog_title = Logger._get_dialog_title(title, custom_title) dialog_title = Logger._get_dialog_title(title, custom_title)
dialog_title_formatted = Logger._format_dialog_title(dialog_title) dialog_title_formatted = Logger._format_dialog_title(dialog_title, dialog_color)
dialog_content = Logger.format_content(content, LINE_WIDTH, center_content) dialog_content = Logger.format_content(
content,
LINE_WIDTH,
dialog_color,
center_content,
)
top = Logger._format_top_border(dialog_color) top = Logger._format_top_border(dialog_color)
bottom = Logger._format_bottom_border() bottom = Logger._format_bottom_border(dialog_color)
print("\n" * margin_top) print("\n" * margin_top)
print( print(
@@ -133,39 +110,45 @@ class Logger:
@staticmethod @staticmethod
def _get_dialog_color( def _get_dialog_color(
title: DialogType, custom_color: DialogCustomColor | None = None title: DialogType, custom_color: Color | None = None
) -> str: ) -> Color:
if title == DialogType.CUSTOM and custom_color: if title == DialogType.CUSTOM and custom_color:
return str(custom_color.value) return custom_color
color: str = title.value[1] if title.value[1] else DialogCustomColor.WHITE.value color: Color = title.value[1] if title.value[1] else Color.WHITE
return color return color
@staticmethod @staticmethod
def _format_top_border(color: str) -> str: def _format_top_border(color: Color) -> str:
return f"{color}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓" _border = Color.apply(
"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", color
@staticmethod
def _format_bottom_border() -> str:
return (
f"\n┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛{RESET_FORMAT}"
) )
return _border
@staticmethod @staticmethod
def _format_dialog_title(title: str | None) -> str: def _format_bottom_border(color: Color) -> str:
if title is not None: _border = Color.apply(
return textwrap.dedent(f""" "\n┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛", color
{title:^{LINE_WIDTH}} )
┠───────────────────────────────────────────────────────┨ return _border
""")
else: @staticmethod
def _format_dialog_title(title: str | None, color: Color) -> str:
if title is None:
return "\n" return "\n"
_title = Color.apply(f"{title:^{LINE_WIDTH}}\n", color)
_title += Color.apply(
"┠───────────────────────────────────────────────────────┨\n", color
)
return _title
@staticmethod @staticmethod
def format_content( def format_content(
content: List[str], content: List[str],
line_width: int, line_width: int,
color: Color = Color.WHITE,
center_content: bool = False, center_content: bool = False,
border_left: str = "", border_left: str = "",
border_right: str = "", border_right: str = "",
@@ -184,11 +167,13 @@ class Logger:
if not center_content: if not center_content:
formatted_lines = [ formatted_lines = [
f"{border_left} {line:<{line_width}} {border_right}" for line in lines Color.apply(f"{border_left} {line:<{line_width}} {border_right}", color)
for line in lines
] ]
else: else:
formatted_lines = [ formatted_lines = [
f"{border_left} {line:^{line_width}} {border_right}" for line in lines Color.apply(f"{border_left} {line:^{line_width}} {border_right}", color)
for line in lines
] ]
return "\n".join(formatted_lines) return "\n".join(formatted_lines)

View File

@@ -22,9 +22,9 @@ from components.klipper_firmware.menus.klipper_flash_menu import (
) )
from components.moonraker import MOONRAKER_DIR from components.moonraker import MOONRAKER_DIR
from components.moonraker.moonraker import Moonraker from components.moonraker.moonraker import Moonraker
from core.constants import COLOR_YELLOW, RESET_FORMAT
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.types.color import Color
from procedures.system import change_system_hostname from procedures.system import change_system_hostname
from utils.git_utils import rollback_repository from utils.git_utils import rollback_repository
@@ -34,6 +34,8 @@ from utils.git_utils import rollback_repository
class AdvancedMenu(BaseMenu): class AdvancedMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None: def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.title = "Advanced Menu"
self.title_color = Color.YELLOW
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
@@ -53,13 +55,8 @@ class AdvancedMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ Advanced Menu ] "
color = COLOR_YELLOW
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent( menu = textwrap.dedent(
f""" """
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────┬───────────────────────────╢ ╟───────────────────────────┬───────────────────────────╢
║ Klipper Firmware: │ Repository Rollback: ║ ║ Klipper Firmware: │ Repository Rollback: ║
║ 1) [Build] │ 5) [Klipper] ║ ║ 1) [Build] │ 5) [Klipper] ║

View File

@@ -23,9 +23,9 @@ from components.webui_client.client_utils import (
) )
from components.webui_client.fluidd_data import FluiddData from components.webui_client.fluidd_data import FluiddData
from components.webui_client.mainsail_data import MainsailData from components.webui_client.mainsail_data import MainsailData
from core.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.types.color import Color
from utils.common import backup_printer_config_dir from utils.common import backup_printer_config_dir
@@ -34,6 +34,8 @@ from utils.common import backup_printer_config_dir
class BackupMenu(BaseMenu): class BackupMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None: def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.title = "Backup Menu"
self.title_color = Color.GREEN
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
@@ -55,14 +57,11 @@ class BackupMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ Backup Menu ] " line1 = Color.apply(
line1 = f"{COLOR_YELLOW}INFO: Backups are located in '~/kiauh-backups'{RESET_FORMAT}" "INFO: Backups are located in '~/kiauh-backups'", Color.YELLOW
color = COLOR_CYAN )
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
{line1:^62} {line1:^62}
╟───────────────────────────┬───────────────────────────╢ ╟───────────────────────────┬───────────────────────────╢

View File

@@ -14,17 +14,13 @@ import sys
import textwrap import textwrap
import traceback import traceback
from abc import abstractmethod from abc import abstractmethod
from enum import Enum
from typing import Dict, Type from typing import Dict, Type
from core.constants import (
COLOR_CYAN,
COLOR_GREEN,
COLOR_RED,
COLOR_YELLOW,
RESET_FORMAT,
)
from core.logger import Logger from core.logger import Logger
from core.menus import FooterType, Option from core.menus import FooterType, Option
from core.spinner import Spinner
from core.types.color import Color
from utils.input_utils import get_selection_input from utils.input_utils import get_selection_input
@@ -36,14 +32,14 @@ def print_header() -> None:
line1 = " [ KIAUH ] " line1 = " [ KIAUH ] "
line2 = "Klipper Installation And Update Helper" line2 = "Klipper Installation And Update Helper"
line3 = "" line3 = ""
color = COLOR_CYAN color = Color.CYAN
count = 62 - len(color) - len(RESET_FORMAT) count = 62 - len(str(color)) - len(str(Color.RST))
header = textwrap.dedent( header = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗ ╔═══════════════════════════════════════════════════════╗
{color}{line1:~^{count}}{RESET_FORMAT} {Color.apply(f"{line1:~^{count}}", color)}
{color}{line2:^{count}}{RESET_FORMAT} {Color.apply(f"{line2:^{count}}", color)}
{color}{line3:~^{count}}{RESET_FORMAT} {Color.apply(f"{line3:~^{count}}", color)}
╚═══════════════════════════════════════════════════════╝ ╚═══════════════════════════════════════════════════════╝
""" """
)[1:] )[1:]
@@ -52,11 +48,11 @@ def print_header() -> None:
def print_quit_footer() -> None: def print_quit_footer() -> None:
text = "Q) Quit" text = "Q) Quit"
color = COLOR_RED color = Color.RED
count = 62 - len(color) - len(RESET_FORMAT) count = 62 - len(str(color)) - len(str(Color.RST))
footer = textwrap.dedent( footer = textwrap.dedent(
f""" f"""
{color}{text:^{count}}{RESET_FORMAT} {color}{text:^{count}}{Color.RST}
╚═══════════════════════════════════════════════════════╝ ╚═══════════════════════════════════════════════════════╝
""" """
)[1:] )[1:]
@@ -65,11 +61,11 @@ def print_quit_footer() -> None:
def print_back_footer() -> None: def print_back_footer() -> None:
text = "B) « Back" text = "B) « Back"
color = COLOR_GREEN color = Color.GREEN
count = 62 - len(color) - len(RESET_FORMAT) count = 62 - len(str(color)) - len(str(Color.RST))
footer = textwrap.dedent( footer = textwrap.dedent(
f""" f"""
{color}{text:^{count}}{RESET_FORMAT} {color}{text:^{count}}{Color.RST}
╚═══════════════════════════════════════════════════════╝ ╚═══════════════════════════════════════════════════════╝
""" """
)[1:] )[1:]
@@ -79,12 +75,12 @@ def print_back_footer() -> None:
def print_back_help_footer() -> None: def print_back_help_footer() -> None:
text1 = "B) « Back" text1 = "B) « Back"
text2 = "H) Help [?]" text2 = "H) Help [?]"
color1 = COLOR_GREEN color1 = Color.GREEN
color2 = COLOR_YELLOW color2 = Color.YELLOW
count = 34 - len(color1) - len(RESET_FORMAT) count = 34 - len(str(color1)) - len(str(Color.RST))
footer = textwrap.dedent( footer = textwrap.dedent(
f""" f"""
{color1}{text1:^{count}}{RESET_FORMAT}{color2}{text2:^{count}}{RESET_FORMAT} {color1}{text1:^{count}}{Color.RST}{color2}{text2:^{count}}{Color.RST}
╚═══════════════════════════╧═══════════════════════════╝ ╚═══════════════════════════╧═══════════════════════════╝
""" """
)[1:] )[1:]
@@ -95,6 +91,11 @@ def print_blank_footer() -> None:
print("╚═══════════════════════════════════════════════════════╝") print("╚═══════════════════════════════════════════════════════╝")
class MenuTitleStyle(Enum):
PLAIN = "plain"
STYLED = "styled"
class PostInitCaller(type): class PostInitCaller(type):
def __call__(cls, *args, **kwargs): def __call__(cls, *args, **kwargs):
obj = type.__call__(cls, *args, **kwargs) obj = type.__call__(cls, *args, **kwargs)
@@ -110,6 +111,14 @@ class BaseMenu(metaclass=PostInitCaller):
default_option: Option = None default_option: Option = None
input_label_txt: str = "Perform action" input_label_txt: str = "Perform action"
header: bool = False header: bool = False
loading_msg: str = ""
spinner: Spinner | None = None
title: str = ""
title_style: MenuTitleStyle = MenuTitleStyle.STYLED
title_color: Color = Color.WHITE
previous_menu: Type[BaseMenu] | None = None previous_menu: Type[BaseMenu] | None = None
help_menu: Type[BaseMenu] | None = None help_menu: Type[BaseMenu] | None = None
footer_type: FooterType = FooterType.BACK footer_type: FooterType = FooterType.BACK
@@ -160,7 +169,32 @@ class BaseMenu(metaclass=PostInitCaller):
def print_menu(self) -> None: def print_menu(self) -> None:
raise NotImplementedError raise NotImplementedError
def print_footer(self) -> None: def is_loading(self, state: bool) -> None:
if not self.spinner and state:
self.spinner = Spinner(self.loading_msg)
self.spinner.start()
else:
self.spinner.stop()
self.spinner = None
def __print_menu_title(self) -> None:
count = 62 - len(str(self.title_color)) - len(str(Color.RST))
menu_title = "╔═══════════════════════════════════════════════════════╗\n"
if self.title:
title = (
f" [ {self.title} ] "
if self.title_style == MenuTitleStyle.STYLED
else self.title
)
line = (
f"{title:~^{count}}"
if self.title_style == MenuTitleStyle.STYLED
else f"{title:^{count}}"
)
menu_title += f"{Color.apply(line, self.title_color)}\n"
print(menu_title, end="")
def __print_footer(self) -> None:
if self.footer_type is FooterType.QUIT: if self.footer_type is FooterType.QUIT:
print_quit_footer() print_quit_footer()
elif self.footer_type is FooterType.BACK: elif self.footer_type is FooterType.BACK:
@@ -172,16 +206,17 @@ class BaseMenu(metaclass=PostInitCaller):
else: else:
raise NotImplementedError("FooterType not correctly implemented!") raise NotImplementedError("FooterType not correctly implemented!")
def display_menu(self) -> None: def __display_menu(self) -> None:
if self.header: if self.header:
print_header() print_header()
self.__print_menu_title()
self.print_menu() self.print_menu()
self.print_footer() self.__print_footer()
def run(self) -> None: def run(self) -> None:
"""Start the menu lifecycle. When this function returns, the lifecycle of the menu ends.""" """Start the menu lifecycle. When this function returns, the lifecycle of the menu ends."""
try: try:
self.display_menu() self.__display_menu()
option = get_selection_input(self.input_label_txt, self.options) option = get_selection_input(self.input_label_txt, self.options)
selected_option: Option = self.options.get(option) selected_option: Option = self.options.get(option)

View File

@@ -22,10 +22,10 @@ from components.webui_client.client_setup import install_client
from components.webui_client.fluidd_data import FluiddData from components.webui_client.fluidd_data import FluiddData
from components.webui_client.mainsail_data import MainsailData from components.webui_client.mainsail_data import MainsailData
from components.webui_client.menus.client_install_menu import ClientInstallMenu from components.webui_client.menus.client_install_menu import ClientInstallMenu
from core.constants import COLOR_GREEN, RESET_FORMAT
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.settings.kiauh_settings import KiauhSettings from core.settings.kiauh_settings import KiauhSettings
from core.types.color import Color
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
@@ -33,6 +33,8 @@ from core.settings.kiauh_settings import KiauhSettings
class InstallMenu(BaseMenu): class InstallMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None: def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.title = "Installation Menu"
self.title_color = Color.GREEN
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
@@ -53,13 +55,8 @@ class InstallMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ Installation Menu ] "
color = COLOR_GREEN
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent( menu = textwrap.dedent(
f""" """
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────┬───────────────────────────╢ ╟───────────────────────────┬───────────────────────────╢
║ Firmware & API: │ Touchscreen GUI: ║ ║ Firmware & API: │ Touchscreen GUI: ║
║ 1) [Klipper] │ 7) [KlipperScreen] ║ ║ 1) [Klipper] │ 7) [KlipperScreen] ║

View File

@@ -23,14 +23,6 @@ from components.webui_client.client_utils import (
) )
from components.webui_client.fluidd_data import FluiddData from components.webui_client.fluidd_data import FluiddData
from components.webui_client.mainsail_data import MainsailData from components.webui_client.mainsail_data import MainsailData
from core.constants import (
COLOR_CYAN,
COLOR_GREEN,
COLOR_MAGENTA,
COLOR_RED,
COLOR_YELLOW,
RESET_FORMAT,
)
from core.logger import Logger from core.logger import Logger
from core.menus import FooterType from core.menus import FooterType
from core.menus.advanced_menu import AdvancedMenu from core.menus.advanced_menu import AdvancedMenu
@@ -40,7 +32,8 @@ from core.menus.install_menu import InstallMenu
from core.menus.remove_menu import RemoveMenu from core.menus.remove_menu import RemoveMenu
from core.menus.settings_menu import SettingsMenu from core.menus.settings_menu import SettingsMenu
from core.menus.update_menu import UpdateMenu from core.menus.update_menu import UpdateMenu
from core.types import ComponentStatus, StatusMap, StatusText from core.types.color import Color
from core.types.component_status import ComponentStatus, StatusMap, StatusText
from extensions.extensions_menu import ExtensionsMenu from extensions.extensions_menu import ExtensionsMenu
from utils.common import get_kiauh_version, trunc_string from utils.common import get_kiauh_version, trunc_string
@@ -52,6 +45,8 @@ class MainMenu(BaseMenu):
super().__init__() super().__init__()
self.header: bool = True self.header: bool = True
self.title = "Main Menu"
self.title_color = Color.CYAN
self.footer_type: FooterType = FooterType.QUIT self.footer_type: FooterType = FooterType.QUIT
self.version = "" self.version = ""
@@ -83,7 +78,7 @@ class MainMenu(BaseMenu):
setattr( setattr(
self, self,
f"{var}_status", f"{var}_status",
f"{COLOR_RED}Not installed{RESET_FORMAT}", Color.apply("Not installed", Color.RED),
) )
def _fetch_status(self) -> None: def _fetch_status(self) -> None:
@@ -109,34 +104,30 @@ class MainMenu(BaseMenu):
count_txt = f": {instance_count}" count_txt = f": {instance_count}"
setattr(self, f"{name}_status", self._format_by_code(code, status, count_txt)) setattr(self, f"{name}_status", self._format_by_code(code, status, count_txt))
setattr(self, f"{name}_owner", f"{COLOR_CYAN}{owner}{RESET_FORMAT}") setattr(self, f"{name}_owner", Color.apply(owner, Color.CYAN))
setattr(self, f"{name}_repo", f"{COLOR_CYAN}{repo}{RESET_FORMAT}") setattr(self, f"{name}_repo", Color.apply(repo, Color.CYAN))
def _format_by_code(self, code: int, status: str, count: str) -> str: def _format_by_code(self, code: int, status: str, count: str) -> str:
color = COLOR_RED color = Color.RED
if code == 0: if code == 0:
color = COLOR_RED color = Color.RED
elif code == 1: elif code == 1:
color = COLOR_YELLOW color = Color.YELLOW
elif code == 2: elif code == 2:
color = COLOR_GREEN color = Color.GREEN
return f"{color}{status}{count}{RESET_FORMAT}" return Color.apply(f"{status}{count}", color)
def print_menu(self) -> None: def print_menu(self) -> None:
self._fetch_status() self._fetch_status()
header = " [ Main Menu ] " footer1 = Color.apply(self.version, Color.CYAN)
footer1 = f"{COLOR_CYAN}{self.version}{RESET_FORMAT}" link = Color.apply("https://git.io/JnmlX", Color.MAGENTA)
footer2 = f"Changelog: {COLOR_MAGENTA}https://git.io/JnmlX{RESET_FORMAT}" footer2 = f"Changelog: {link}"
color = COLOR_CYAN
count = 62 - len(color) - len(RESET_FORMAT)
pad1 = 32 pad1 = 32
pad2 = 26 pad2 = 26
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟──────────────────┬────────────────────────────────────╢ ╟──────────────────┬────────────────────────────────────╢
║ 0) [Log-Upload] │ Klipper: {self.kl_status:<{pad1}} ║ 0) [Log-Upload] │ Klipper: {self.kl_status:<{pad1}}
║ │ Owner: {self.kl_owner:<{pad1}} ║ │ Owner: {self.kl_owner:<{pad1}}

View File

@@ -20,9 +20,9 @@ from components.moonraker.menus.moonraker_remove_menu import (
from components.webui_client.fluidd_data import FluiddData from components.webui_client.fluidd_data import FluiddData
from components.webui_client.mainsail_data import MainsailData from components.webui_client.mainsail_data import MainsailData
from components.webui_client.menus.client_remove_menu import ClientRemoveMenu from components.webui_client.menus.client_remove_menu import ClientRemoveMenu
from core.constants import COLOR_RED, RESET_FORMAT
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.types.color import Color
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
@@ -30,6 +30,8 @@ from core.menus.base_menu import BaseMenu
class RemoveMenu(BaseMenu): class RemoveMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None: def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.title = "Remove Menu"
self.title_color = Color.RED
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
@@ -48,13 +50,8 @@ class RemoveMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ Remove Menu ] "
color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent( menu = textwrap.dedent(
f""" """
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ INFO: Configurations and/or any backups will be kept! ║ ║ INFO: Configurations and/or any backups will be kept! ║
╟───────────────────────────┬───────────────────────────╢ ╟───────────────────────────┬───────────────────────────╢

View File

@@ -13,11 +13,11 @@ from typing import Literal, Tuple, Type
from components.klipper.klipper_utils import get_klipper_status from components.klipper.klipper_utils import get_klipper_status
from components.moonraker.moonraker_utils import get_moonraker_status from components.moonraker.moonraker_utils import get_moonraker_status
from core.constants import COLOR_CYAN, COLOR_GREEN, RESET_FORMAT
from core.logger import DialogType, Logger from core.logger import DialogType, Logger
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.settings.kiauh_settings import KiauhSettings, RepoSettings from core.settings.kiauh_settings import KiauhSettings, RepoSettings
from core.types.color import Color
from procedures.switch_repo import run_switch_repo_routine from procedures.switch_repo import run_switch_repo_routine
from utils.input_utils import get_confirm, get_string_input from utils.input_utils import get_confirm, get_string_input
@@ -27,6 +27,8 @@ from utils.input_utils import get_confirm, get_string_input
class SettingsMenu(BaseMenu): class SettingsMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None: def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.title = "Settings Menu"
self.title_color = Color.CYAN
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.klipper_status = get_klipper_status() self.klipper_status = get_klipper_status()
self.moonraker_status = get_moonraker_status() self.moonraker_status = get_moonraker_status()
@@ -50,25 +52,21 @@ class SettingsMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ KIAUH Settings ] " color = Color.CYAN
color, rst = COLOR_CYAN, RESET_FORMAT checked = f"[{Color.apply('x', Color.GREEN)}]"
count = 62 - len(color) - len(rst)
checked = f"[{COLOR_GREEN}x{rst}]"
unchecked = "[ ]" unchecked = "[ ]"
kl_repo: str = f"{color}{self.klipper_status.repo}{rst}" kl_repo: str = Color.apply(self.klipper_status.repo, color)
kl_branch: str = f"{color}{self.klipper_status.branch}{rst}" kl_branch: str = Color.apply(self.klipper_status.branch, color)
kl_owner: str = f"{color}{self.klipper_status.owner}{rst}" kl_owner: str = Color.apply(self.klipper_status.owner, color)
mr_repo: str = f"{color}{self.moonraker_status.repo}{rst}" mr_repo: str = Color.apply(self.moonraker_status.repo, color)
mr_branch: str = f"{color}{self.moonraker_status.branch}{rst}" mr_branch: str = Color.apply(self.moonraker_status.branch, color)
mr_owner: str = f"{color}{self.moonraker_status.owner}{rst}" mr_owner: str = Color.apply(self.moonraker_status.owner, color)
o1 = checked if self.mainsail_unstable else unchecked o1 = checked if self.mainsail_unstable else unchecked
o2 = checked if self.fluidd_unstable else unchecked o2 = checked if self.fluidd_unstable else unchecked
o3 = checked if self.auto_backups_enabled else unchecked o3 = checked if self.auto_backups_enabled else unchecked
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{rst}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
║ Klipper: ║ ║ Klipper: ║
║ ● Repo: {kl_repo:51} ║ ● Repo: {kl_repo:51}

View File

@@ -32,17 +32,11 @@ from components.webui_client.client_utils import (
) )
from components.webui_client.fluidd_data import FluiddData from components.webui_client.fluidd_data import FluiddData
from components.webui_client.mainsail_data import MainsailData from components.webui_client.mainsail_data import MainsailData
from core.constants import (
COLOR_GREEN,
COLOR_RED,
COLOR_YELLOW,
RESET_FORMAT,
)
from core.logger import DialogType, Logger from core.logger import DialogType, Logger
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.spinner import Spinner from core.types.color import Color
from core.types import ComponentStatus from core.types.component_status import ComponentStatus
from utils.input_utils import get_confirm from utils.input_utils import get_confirm
from utils.sys_utils import ( from utils.sys_utils import (
get_upgradable_packages, get_upgradable_packages,
@@ -56,6 +50,11 @@ from utils.sys_utils import (
class UpdateMenu(BaseMenu): class UpdateMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None: def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.loading_msg = "Loading update menu, please wait"
self.is_loading(True)
self.title = "Update Menu"
self.title_color = Color.GREEN
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.packages: List[str] = [] self.packages: List[str] = []
@@ -123,6 +122,9 @@ class UpdateMenu(BaseMenu):
}, },
} }
self._fetch_update_status()
self.is_loading(False)
def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.main_menu import MainMenu from core.menus.main_menu import MainMenu
@@ -143,29 +145,16 @@ class UpdateMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
spinner = Spinner("Loading update menu, please wait", color="green")
spinner.start()
self._fetch_update_status()
spinner.stop()
header = " [ Update Menu ] "
color = COLOR_GREEN
count = 62 - len(color) - len(RESET_FORMAT)
sysupgrades: str = "No upgrades available." sysupgrades: str = "No upgrades available."
padding = 29 padding = 29
if self.package_count > 0: if self.package_count > 0:
sysupgrades = ( sysupgrades = Color.apply(
f"{COLOR_GREEN}{self.package_count} upgrades available!{RESET_FORMAT}" f"{self.package_count} upgrades available!", Color.GREEN
) )
padding = 38 padding = 38
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────┬───────────────┬───────────────╢ ╟───────────────────────┬───────────────┬───────────────╢
║ a) Update all │ │ ║ ║ a) Update all │ │ ║
║ │ Current: │ Latest: ║ ║ │ Current: │ Latest: ║
@@ -265,15 +254,15 @@ class UpdateMenu(BaseMenu):
self.package_count = len(self.packages) self.package_count = len(self.packages)
def _format_local_status(self, local_version, remote_version) -> str: def _format_local_status(self, local_version, remote_version) -> str:
color = COLOR_RED color = Color.RED
if not local_version: if not local_version:
color = COLOR_RED color = Color.RED
elif local_version == remote_version: elif local_version == remote_version:
color = COLOR_GREEN color = Color.GREEN
elif local_version != remote_version: elif local_version != remote_version:
color = COLOR_YELLOW color = Color.YELLOW
return f"{color}{local_version or '-'}{RESET_FORMAT}" return Color.apply(local_version or "-", color)
def _set_status_data(self, name: str, status_fn: Callable, *args) -> None: def _set_status_data(self, name: str, status_fn: Callable, *args) -> None:
comp_status: ComponentStatus = status_fn(*args) comp_status: ComponentStatus = status_fn(*args)
@@ -288,9 +277,9 @@ class UpdateMenu(BaseMenu):
local_status = self.status_data[name].get("local", None) local_status = self.status_data[name].get("local", None)
remote_status = self.status_data[name].get("remote", None) remote_status = self.status_data[name].get("remote", None)
color = COLOR_GREEN if remote_status else COLOR_RED color = Color.GREEN if remote_status else Color.RED
local_txt = self._format_local_status(local_status, remote_status) local_txt = self._format_local_status(local_status, remote_status)
remote_txt = f"{color}{remote_status or '-'}{RESET_FORMAT}" remote_txt = Color.apply(remote_status or "-", color)
setattr(self, f"{name}_local", local_txt) setattr(self, f"{name}_local", local_txt)
setattr(self, f"{name}_remote", remote_txt) setattr(self, f"{name}_remote", remote_txt)

View File

@@ -3,13 +3,7 @@ import threading
import time import time
from typing import List, Literal from typing import List, Literal
from core.constants import ( from core.types.color import Color
COLOR_GREEN,
COLOR_RED,
COLOR_WHITE,
COLOR_YELLOW,
RESET_FORMAT,
)
SpinnerColor = Literal["white", "red", "green", "yellow"] SpinnerColor = Literal["white", "red", "green", "yellow"]
@@ -18,21 +12,18 @@ class Spinner:
def __init__( def __init__(
self, self,
message: str = "Loading", message: str = "Loading",
color: SpinnerColor = "white",
interval: float = 0.2, interval: float = 0.2,
) -> None: ) -> None:
self.message = f"{message} ..." self.message = f"{message} ..."
self.interval = interval self.interval = interval
self._stop_event = threading.Event() self._stop_event = threading.Event()
self._thread = threading.Thread(target=self._animate) self._thread = threading.Thread(target=self._animate)
self._color = ""
self._set_color(color)
def _animate(self) -> None: def _animate(self) -> None:
animation: List[str] = ["", "", "", "", "", "", "", "", "", ""] animation: List[str] = ["", "", "", "", "", "", "", "", "", ""]
while not self._stop_event.is_set(): while not self._stop_event.is_set():
for char in animation: for char in animation:
sys.stdout.write(f"\r{self._color}{char}{RESET_FORMAT} {self.message}") sys.stdout.write(f"\r{Color.GREEN}{char}{Color.RST} {self.message}")
sys.stdout.flush() sys.stdout.flush()
time.sleep(self.interval) time.sleep(self.interval)
if self._stop_event.is_set(): if self._stop_event.is_set():
@@ -40,16 +31,6 @@ class Spinner:
sys.stdout.write("\r" + " " * (len(self.message) + 1) + "\r") sys.stdout.write("\r" + " " * (len(self.message) + 1) + "\r")
sys.stdout.flush() sys.stdout.flush()
def _set_color(self, color: SpinnerColor) -> None:
if color == "white":
self._color = COLOR_WHITE
elif color == "red":
self._color = COLOR_RED
elif color == "green":
self._color = COLOR_GREEN
elif color == "yellow":
self._color = COLOR_YELLOW
def start(self) -> None: def start(self) -> None:
self._stop_event.clear() self._stop_event.clear()
if not self._thread.is_alive(): if not self._thread.is_alive():

View File

29
kiauh/core/types/color.py Normal file
View File

@@ -0,0 +1,29 @@
# ======================================================================= #
# Copyright (C) 2020 - 2024 Dominik Willner <th33xitus@gmail.com> #
# #
# This file is part of KIAUH - Klipper Installation And Update Helper #
# https://github.com/dw-0/kiauh #
# #
# This file may be distributed under the terms of the GNU GPLv3 license #
# ======================================================================= #
from __future__ import annotations
from enum import Enum
class Color(Enum):
WHITE = "\033[37m" # white
MAGENTA = "\033[35m" # magenta
GREEN = "\033[92m" # bright green
YELLOW = "\033[93m" # bright yellow
RED = "\033[91m" # bright red
CYAN = "\033[96m" # bright cyan
RST = "\033[0m" # reset format
def __str__(self):
return self.value
@staticmethod
def apply(text: str | int, color: "Color") -> str:
"""Apply a given color to a given text string."""
return f"{color}{text}{Color.RST}"

View File

@@ -15,10 +15,10 @@ import textwrap
from pathlib import Path from pathlib import Path
from typing import Dict, List, Type from typing import Dict, List, Type
from core.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT
from core.logger import Logger from core.logger import Logger
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.types.color import Color
from extensions import EXTENSION_ROOT from extensions import EXTENSION_ROOT
from extensions.base_extension import BaseExtension from extensions.base_extension import BaseExtension
@@ -28,6 +28,8 @@ from extensions.base_extension import BaseExtension
class ExtensionsMenu(BaseMenu): class ExtensionsMenu(BaseMenu):
def __init__(self, previous_menu: Type[BaseMenu] | None = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.title = "Extensions Menu"
self.title_color = Color.CYAN
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.extensions: Dict[str, BaseExtension] = self.discover_extensions() self.extensions: Dict[str, BaseExtension] = self.discover_extensions()
@@ -82,14 +84,9 @@ class ExtensionsMenu(BaseMenu):
ExtensionSubmenu(kwargs.get("opt_data"), self.__class__).run() ExtensionSubmenu(kwargs.get("opt_data"), self.__class__).run()
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ Extensions Menu ] " line1 = Color.apply("Available Extensions:", Color.YELLOW)
color = COLOR_CYAN
line1 = f"{COLOR_YELLOW}Available Extensions:{RESET_FORMAT}"
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
{line1:<62} {line1:<62}
║ ║ ║ ║
@@ -112,6 +109,8 @@ class ExtensionSubmenu(BaseMenu):
self, extension: BaseExtension, previous_menu: Type[BaseMenu] | None = None self, extension: BaseExtension, previous_menu: Type[BaseMenu] | None = None
): ):
super().__init__() super().__init__()
self.title = extension.metadata.get("display_name")
self.title_color = Color.YELLOW
self.extension = extension self.extension = extension
self.previous_menu: Type[BaseMenu] | None = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
@@ -129,9 +128,6 @@ class ExtensionSubmenu(BaseMenu):
self.options["2"] = Option(self.extension.remove_extension) self.options["2"] = Option(self.extension.remove_extension)
def print_menu(self) -> None: def print_menu(self) -> None:
header = f" [ {self.extension.metadata.get('display_name')} ] "
color = COLOR_YELLOW
count = 62 - len(color) - len(RESET_FORMAT)
line_width = 53 line_width = 53
description: List[str] = self.extension.metadata.get("description", []) description: List[str] = self.extension.metadata.get("description", [])
description_text = Logger.format_content( description_text = Logger.format_content(
@@ -142,9 +138,7 @@ class ExtensionSubmenu(BaseMenu):
) )
menu = textwrap.dedent( menu = textwrap.dedent(
f""" """
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
""" """
)[1:] )[1:]

View File

@@ -20,10 +20,10 @@ from components.klipper.klipper_dialogs import (
DisplayType, DisplayType,
print_instance_overview, print_instance_overview,
) )
from core.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT
from core.logger import Logger from core.logger import Logger
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from core.types.color import Color
from extensions.base_extension import BaseExtension from extensions.base_extension import BaseExtension
from utils.git_utils import git_clone_wrapper from utils.git_utils import git_clone_wrapper
from utils.input_utils import get_selection_input from utils.input_utils import get_selection_input
@@ -80,6 +80,8 @@ class MainsailThemeInstallMenu(BaseMenu):
def __init__(self, instances: List[Klipper]): def __init__(self, instances: List[Klipper]):
super().__init__() super().__init__()
self.title = "Mainsail Theme Installer"
self.title_color = Color.YELLOW
self.themes: List[ThemeData] = self.load_themes() self.themes: List[ThemeData] = self.load_themes()
self.instances = instances self.instances = instances
@@ -97,14 +99,11 @@ class MainsailThemeInstallMenu(BaseMenu):
} }
def print_menu(self) -> None: def print_menu(self) -> None:
header = " [ Mainsail Theme Installer ] " line1 = Color.apply(
color = COLOR_YELLOW "A preview of each Mainsail theme can be found here:", Color.YELLOW
line1 = f"{COLOR_CYAN}A preview of each Mainsail theme can be found here:{RESET_FORMAT}" )
count = 62 - len(color) - len(RESET_FORMAT)
menu = textwrap.dedent( menu = textwrap.dedent(
f""" f"""
╔═══════════════════════════════════════════════════════╗
{color}{header:~^{count}}{RESET_FORMAT}
╟───────────────────────────────────────────────────────╢ ╟───────────────────────────────────────────────────────╢
{line1:<62} {line1:<62}
║ https://docs.mainsail.xyz/theming/themes ║ ║ https://docs.mainsail.xyz/theming/themes ║

View File

@@ -16,13 +16,12 @@ from typing import Dict, List, Literal, Optional, Set
from components.klipper.klipper import Klipper from components.klipper.klipper import Klipper
from components.moonraker.moonraker import Moonraker from components.moonraker.moonraker import Moonraker
from core.constants import ( from core.constants import (
COLOR_CYAN,
GLOBAL_DEPS, GLOBAL_DEPS,
PRINTER_DATA_BACKUP_DIR, PRINTER_DATA_BACKUP_DIR,
RESET_FORMAT,
) )
from core.logger import DialogType, Logger from core.logger import DialogType, Logger
from core.types import ComponentStatus, StatusCode from core.types.color import Color
from core.types.component_status import ComponentStatus, StatusCode
from utils.git_utils import ( from utils.git_utils import (
get_current_branch, get_current_branch,
get_local_commit, get_local_commit,
@@ -83,7 +82,7 @@ def check_install_dependencies(
Logger.print_status("Installing dependencies ...") Logger.print_status("Installing dependencies ...")
Logger.print_info("The following packages need installation:") Logger.print_info("The following packages need installation:")
for r in requirements: for r in requirements:
print(f"{COLOR_CYAN}{r}{RESET_FORMAT}") print(Color.apply(f"{r}", Color.CYAN))
update_system_package_lists(silent=False) update_system_package_lists(silent=False)
install_system_packages(requirements) install_system_packages(requirements)

View File

@@ -11,8 +11,9 @@ from __future__ import annotations
import re import re
from typing import Dict, List from typing import Dict, List
from core.constants import COLOR_CYAN, INVALID_CHOICE, RESET_FORMAT from core.constants import INVALID_CHOICE
from core.logger import Logger from core.logger import Logger
from core.types.color import Color
def get_confirm(question: str, default_choice=True, allow_go_back=False) -> bool | None: def get_confirm(question: str, default_choice=True, allow_go_back=False) -> bool | None:
@@ -151,7 +152,7 @@ def format_question(question: str, default=None) -> str:
if default is not None: if default is not None:
formatted_q += f" (default={default})" formatted_q += f" (default={default})"
return f"{COLOR_CYAN}###### {formatted_q}: {RESET_FORMAT}" return Color.apply(f"###### {formatted_q}: ", Color.CYAN)
def validate_number_input(value: str, min_count: int, max_count: int | None) -> int: def validate_number_input(value: str, min_count: int, max_count: int | None) -> int: