Skip to content

feat: Menu Status Indicator#4663

Open
CooperWang0912 wants to merge 8 commits into
archlinux:masterfrom
CooperWang0912:feat/menu-status-indicator
Open

feat: Menu Status Indicator#4663
CooperWang0912 wants to merge 8 commits into
archlinux:masterfrom
CooperWang0912:feat/menu-status-indicator

Conversation

@CooperWang0912

@CooperWang0912 CooperWang0912 commented Jul 24, 2026

Copy link
Copy Markdown

PR Description

  • Created status indicator for the installation menu
image

Motivation

  • Visually indicate which fields are preventing the user from installing
  • Create instant feedback when user configures a field
  • Provide better user experience for people unfamiliar with the installation process
  • Closes [Feat] Menu Item Status Indicator #4658

Technical Description

  1. Added method _get_status_prefix for menu item completion status, with red exclamation marks for required and uncompleted fields, yellow dots for optional and uncompleted fields, and green checks for completed fields.[¹] The checks are achieved using the following logic:
if item.read_only or item.key in (SpecialMenuKey.SAVE.value, SpecialMenuKey.INSTALL.value, SpecialMenuKey.ABORT.value):
			return ''

		if item.key == 'auth_config':
			auth_config: AuthenticationConfiguration | None = item.value
			is_auth_valid = auth_config is not None and (auth_config.root_enc_password is not None or auth_config.has_superuser())
			if is_auth_valid:
				return '[bold green][✓][/bold green] '
			return '[bold red][!][/bold red] '

		# Standard mandatory or configured item check
		if item.has_value():
			return '[bold green][✓][/bold green] '
		elif item.mandatory:
			return '[bold red][!][/bold red] '
		else:
			return '[bold yellow][•][/bold yellow] '
  1. Added method _update_item_labels that updates the menu render based on _get_status_prefix. The code is as follows:
def _update_item_labels(self) -> None:
		"""
		Re-applies translated titles and status prefixes to all active menu items.
		"""
		raw_options = self._get_menu_options(wrap_actions=False)
		new_options = {o.key: o.text for o in raw_options if o.key is not None}

		for item in self._item_group.items:
			if item.key in new_options:
				base_title = new_options[item.key]
				prefix = self._get_status_prefix(item)
				item.text = f'{prefix}{base_title}'
  1. In order to avoid stale feedback due to background process not instantly updating values, created a wrapper that calls the _update_item_labels when called, and is triggered with _get_menu_options.[²] The implementation is as follows:
def _wrap_action(self, item: MenuItem, action: Callable[..., Any]) -> Callable[..., Any]:
		@wraps(action)
		async def wrapper(*args: Any, **kwargs: Any) -> Any:
			try:
				if inspect.iscoroutinefunction(action):
					result = await action(*args, **kwargs)
				else:
					result = action(*args, **kwargs)
					if inspect.isawaitable(result):
						result = await result

				item.value = result

				return result

			finally:
				self._update_item_labels()

		return wrapper

Tests and Checks

  • I have tested the code!

Notes

[¹] The color scheme and symbols are up for discussion, and changes to these would be fairly straight forward
[²] I am using this primarily to contain the blast radius inside global_menu, and I would be happy to rework the logic if needed

@CooperWang0912
CooperWang0912 requested a review from Torxed as a code owner July 24, 2026 06:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] Menu Item Status Indicator

1 participant