Replace raw Rich markup with PreviewResult dataclass for typed preview levels
This commit is contained in:
parent
885d1aeef9
commit
c5d5135c4a
|
|
@ -149,7 +149,7 @@ class ConfigurationOutput:
|
|||
warnings: list[str] = []
|
||||
|
||||
if not isinstance(self._config.network_config, NetworkConfiguration):
|
||||
warnings.append(tr('Warning: no network configuration selected. Network will need to be set up manually on the installed system.'))
|
||||
warnings.append(tr('No network configuration selected. Network will need to be set up manually on the installed system.'))
|
||||
|
||||
return warnings
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
from typing import override
|
||||
|
||||
from rich.markup import escape as _escape_markup
|
||||
|
||||
from archinstall.default_profiles.profile import GreeterType
|
||||
from archinstall.lib.applications.application_menu import ApplicationMenu
|
||||
from archinstall.lib.args import ArchConfig
|
||||
|
|
@ -35,7 +33,7 @@ from archinstall.lib.pacman.config import PacmanConfig
|
|||
from archinstall.lib.pacman.pacman_menu import PacmanMenu
|
||||
from archinstall.lib.translationhandler import Language, tr, translation_handler
|
||||
from archinstall.tui.ui.components import tui
|
||||
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
|
||||
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup, MsgLevelType, PreviewResult
|
||||
|
||||
|
||||
class GlobalMenu(AbstractMenu[None]):
|
||||
|
|
@ -185,7 +183,6 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
MenuItem(
|
||||
text=tr('Install'),
|
||||
preview_action=self._prev_install_invalid_config,
|
||||
preview_markup=True,
|
||||
key=SpecialMenuKey.INSTALL.value,
|
||||
),
|
||||
MenuItem(
|
||||
|
|
@ -497,42 +494,40 @@ class GlobalMenu(AbstractMenu[None]):
|
|||
|
||||
return None
|
||||
|
||||
def _prev_install_invalid_config(self, item: MenuItem) -> str | None:
|
||||
def _prev_install_invalid_config(self, item: MenuItem) -> str | PreviewResult | list[PreviewResult] | None:
|
||||
self.sync_all_to_config()
|
||||
config_output = ConfigurationOutput(self._arch_config)
|
||||
|
||||
warnings = config_output.get_install_warnings()
|
||||
warnings_text = ''
|
||||
if warnings:
|
||||
warnings_text = f'\n\n[yellow]{_escape_markup(tr("Warnings:"))}\n'
|
||||
for w in warnings:
|
||||
warnings_text += f'- {_escape_markup(w)}\n'
|
||||
warnings_text = warnings_text.rstrip('\n') + '[/yellow]'
|
||||
|
||||
blocks = ''
|
||||
sections: list[PreviewResult] = []
|
||||
|
||||
errors = ''
|
||||
if missing := self._missing_configs():
|
||||
text = f'[red]{_escape_markup(tr("Missing configurations:"))}\n'
|
||||
for m in missing:
|
||||
text += f'- {_escape_markup(m)}\n'
|
||||
blocks += text.rstrip('\n') + '[/red]'
|
||||
errors += f'{tr("Missing configurations:")}\n'
|
||||
errors += '\n'.join(f'- {m}' for m in missing)
|
||||
|
||||
disk_item = self._item_group.find_by_key('disk_config')
|
||||
if disk_item.has_value():
|
||||
if error := self._validate_bootloader():
|
||||
if blocks:
|
||||
blocks += '\n\n'
|
||||
text = f'[red]{_escape_markup(tr("Invalid configuration:"))}\n'
|
||||
text += f'- {_escape_markup(error)}'
|
||||
blocks += text + '[/red]'
|
||||
if errors:
|
||||
errors += '\n\n'
|
||||
errors += f'{tr("Invalid configuration:")}\n- {error}'
|
||||
|
||||
if blocks:
|
||||
return blocks + warnings_text
|
||||
if errors:
|
||||
sections.append(PreviewResult(errors, MsgLevelType.MsgError))
|
||||
else:
|
||||
sections.append(PreviewResult(tr('Ready to install'), MsgLevelType.MsgInfo))
|
||||
|
||||
summary = config_output.as_summary()
|
||||
if summary:
|
||||
return f'[green]{_escape_markup(tr("Ready to install"))}[/green]{warnings_text}\n\n{_escape_markup(summary)}'
|
||||
return f'[green]{_escape_markup(tr("Ready to install"))}[/green]{warnings_text}'
|
||||
if warnings:
|
||||
text = f'{tr("Warnings:")}\n' + '\n'.join(f'- {w}' for w in warnings)
|
||||
sections.append(PreviewResult(text, MsgLevelType.MsgWarning))
|
||||
|
||||
if not errors:
|
||||
summary = config_output.as_summary()
|
||||
if summary:
|
||||
sections.append(PreviewResult(summary, MsgLevelType.MsgNone))
|
||||
|
||||
return sections
|
||||
|
||||
def _prev_profile(self, item: MenuItem) -> str | None:
|
||||
profile_config: ProfileConfiguration | None = item.value
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ from pathlib import Path
|
|||
from archinstall.lib.menu.helpers import Confirmation, Input
|
||||
from archinstall.lib.models.users import Password, PasswordStrength
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.tui.ui.components import InputInfo, InputInfoType, tui
|
||||
from archinstall.tui.ui.components import InputInfo, tui
|
||||
from archinstall.tui.ui.menu_item import MsgLevelType
|
||||
from archinstall.tui.ui.result import ResultType
|
||||
|
||||
|
||||
|
|
@ -20,11 +21,11 @@ async def get_password(
|
|||
return None
|
||||
strength = PasswordStrength.strength(value)
|
||||
if strength in (PasswordStrength.VERY_WEAK, PasswordStrength.WEAK):
|
||||
return InputInfo(message=tr('Password strength: Weak'), info_type=InputInfoType.MsgError)
|
||||
return InputInfo(message=tr('Password strength: Weak'), msg_level=MsgLevelType.MsgError)
|
||||
elif strength == PasswordStrength.MODERATE:
|
||||
return InputInfo(message=tr('Password strength: Moderate'), info_type=InputInfoType.MsgWarning)
|
||||
return InputInfo(message=tr('Password strength: Moderate'), msg_level=MsgLevelType.MsgWarning)
|
||||
elif strength == PasswordStrength.STRONG:
|
||||
return InputInfo(message=tr('Password strength: Strong'), info_type=InputInfoType.MsgInfo)
|
||||
return InputInfo(message=tr('Password strength: Strong'), msg_level=MsgLevelType.MsgInfo)
|
||||
return None
|
||||
|
||||
while True:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import sys
|
|||
from abc import ABC, abstractmethod
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass, replace
|
||||
from enum import Enum, auto
|
||||
from typing import Any, ClassVar, Literal, TypeVar, cast, override
|
||||
|
||||
from rich.text import Text
|
||||
|
|
@ -22,12 +21,39 @@ from textual.worker import WorkerCancelled
|
|||
|
||||
from archinstall.lib.output import debug
|
||||
from archinstall.lib.translationhandler import tr
|
||||
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup
|
||||
from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup, MsgLevelType, PreviewResult
|
||||
from archinstall.tui.ui.result import Result, ResultType
|
||||
|
||||
ValueT = TypeVar('ValueT')
|
||||
|
||||
|
||||
_LEVEL_STYLE: dict[MsgLevelType, str] = {
|
||||
MsgLevelType.MsgNone: '',
|
||||
MsgLevelType.MsgError: 'red',
|
||||
MsgLevelType.MsgWarning: 'bright_yellow',
|
||||
MsgLevelType.MsgInfo: 'green',
|
||||
}
|
||||
|
||||
|
||||
def _update_preview(widget: Label, result: str | PreviewResult | list[PreviewResult] | None) -> None:
|
||||
if result is None:
|
||||
widget.update('')
|
||||
return
|
||||
|
||||
if isinstance(result, str):
|
||||
widget.update(result)
|
||||
elif isinstance(result, PreviewResult):
|
||||
text = Text(result.message, style=_LEVEL_STYLE[result.msg_level])
|
||||
widget.update(text)
|
||||
else:
|
||||
text = Text()
|
||||
for i, section in enumerate(result):
|
||||
if i > 0:
|
||||
text.append('\n\n')
|
||||
text.append(section.message, style=_LEVEL_STYLE[section.msg_level])
|
||||
widget.update(text)
|
||||
|
||||
|
||||
def _translate_bindings(source: BindingsMap | None, target: BindingsMap) -> None:
|
||||
"""Translate binding descriptions from source to target.
|
||||
|
||||
|
|
@ -357,14 +383,9 @@ class OptionListScreen(BaseScreen[ValueT]):
|
|||
item = self._group.find_by_id(item_id)
|
||||
|
||||
if item.preview_action is not None:
|
||||
maybe_preview = item.preview_action(item)
|
||||
|
||||
if maybe_preview is not None:
|
||||
content = Text.from_markup(maybe_preview) if item.preview_markup else maybe_preview
|
||||
preview_widget.update(content)
|
||||
return
|
||||
|
||||
preview_widget.update('')
|
||||
_update_preview(preview_widget, item.preview_action(item))
|
||||
else:
|
||||
_update_preview(preview_widget, None)
|
||||
|
||||
|
||||
class _SelectionList(SelectionList[ValueT]):
|
||||
|
|
@ -601,13 +622,9 @@ class SelectListScreen(BaseScreen[ValueT]):
|
|||
preview_widget = self.query_one('#preview_content', Label)
|
||||
|
||||
if item.preview_action is not None:
|
||||
maybe_preview = item.preview_action(item)
|
||||
if maybe_preview is not None:
|
||||
content = Text.from_markup(maybe_preview) if item.preview_markup else maybe_preview
|
||||
preview_widget.update(content)
|
||||
return
|
||||
|
||||
preview_widget.update('')
|
||||
_update_preview(preview_widget, item.preview_action(item))
|
||||
else:
|
||||
_update_preview(preview_widget, None)
|
||||
|
||||
|
||||
# DEPRECATED: Removed when switching to async
|
||||
|
|
@ -723,14 +740,8 @@ class ConfirmationScreen(BaseScreen[ValueT]):
|
|||
|
||||
if self._preview_header is not None:
|
||||
preview = self.query_one('#preview_content', Label)
|
||||
|
||||
if focused.preview_action is None:
|
||||
preview.update('')
|
||||
else:
|
||||
text = focused.preview_action(focused)
|
||||
if text is not None:
|
||||
content = Text.from_markup(text) if focused.preview_markup else text
|
||||
preview.update(content)
|
||||
result = focused.preview_action(focused) if focused.preview_action else None
|
||||
_update_preview(preview, result)
|
||||
else:
|
||||
button.remove_class('-active')
|
||||
|
||||
|
|
@ -757,16 +768,10 @@ class NotifyScreen(ConfirmationScreen[ValueT]):
|
|||
super().__init__(group, header)
|
||||
|
||||
|
||||
class InputInfoType(Enum):
|
||||
MsgInfo = auto()
|
||||
MsgWarning = auto()
|
||||
MsgError = auto()
|
||||
|
||||
|
||||
@dataclass
|
||||
class InputInfo:
|
||||
message: str
|
||||
info_type: InputInfoType
|
||||
msg_level: MsgLevelType
|
||||
|
||||
|
||||
class InputScreen(BaseScreen[str]):
|
||||
|
|
@ -878,11 +883,11 @@ class InputScreen(BaseScreen[str]):
|
|||
result = self._info_callback(event.value)
|
||||
if result:
|
||||
css_class = ''
|
||||
if result.info_type == InputInfoType.MsgError:
|
||||
if result.msg_level == MsgLevelType.MsgError:
|
||||
css_class = 'input-hint-msg-error'
|
||||
elif result.info_type == InputInfoType.MsgWarning:
|
||||
elif result.msg_level == MsgLevelType.MsgWarning:
|
||||
css_class = 'input-hint-msg-warning'
|
||||
elif result.info_type == InputInfoType.MsgInfo:
|
||||
elif result.msg_level == MsgLevelType.MsgInfo:
|
||||
css_class = 'input-hint-msg-info'
|
||||
info_label.update(result.message)
|
||||
info_label.set_classes(css_class)
|
||||
|
|
@ -1127,14 +1132,7 @@ class TableSelectionScreen(BaseScreen[ValueT]):
|
|||
return
|
||||
|
||||
preview_widget = self.query_one('#preview_content', Label)
|
||||
|
||||
maybe_preview = item.preview_action(item)
|
||||
if maybe_preview is not None:
|
||||
content = Text.from_markup(maybe_preview) if item.preview_markup else maybe_preview
|
||||
preview_widget.update(content)
|
||||
return
|
||||
|
||||
preview_widget.update('')
|
||||
_update_preview(preview_widget, item.preview_action(item))
|
||||
|
||||
def _set_cursor(self, row_index: int) -> None:
|
||||
data_table = self.query_one(DataTable)
|
||||
|
|
@ -1271,6 +1269,7 @@ class _AppInstance(App[ValueT]):
|
|||
background: black;
|
||||
border-left: vkey white 20%;
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, main: InstanceRunnable[ValueT] | Callable[[], Awaitable[ValueT]]) -> None:
|
||||
|
|
|
|||
|
|
@ -1,12 +1,27 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Awaitable, Callable, Iterable
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from enum import Enum, auto
|
||||
from functools import cached_property
|
||||
from typing import Any, ClassVar, Self, override
|
||||
|
||||
from archinstall.lib.translationhandler import tr
|
||||
|
||||
|
||||
class MsgLevelType(Enum):
|
||||
MsgNone = auto()
|
||||
MsgInfo = auto()
|
||||
MsgWarning = auto()
|
||||
MsgError = auto()
|
||||
|
||||
|
||||
@dataclass
|
||||
class PreviewResult:
|
||||
message: str
|
||||
msg_level: MsgLevelType
|
||||
|
||||
|
||||
@dataclass
|
||||
class MenuItem:
|
||||
text: str
|
||||
|
|
@ -18,8 +33,7 @@ class MenuItem:
|
|||
dependencies: list[str | Callable[[], bool]] = field(default_factory=list)
|
||||
dependencies_not: list[str] = field(default_factory=list)
|
||||
display_action: Callable[[Any], str] | None = None
|
||||
preview_action: Callable[[Self], str | None] | None = None
|
||||
preview_markup: bool = False
|
||||
preview_action: Callable[[Self], str | PreviewResult | list[PreviewResult] | None] | None = None
|
||||
key: str | None = None
|
||||
|
||||
_id: str = ''
|
||||
|
|
|
|||
Loading…
Reference in New Issue