variable_display.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. from __future__ import annotations
  2. from typing import TYPE_CHECKING
  3. from rich.console import Console
  4. from rich.table import Table
  5. if TYPE_CHECKING:
  6. from . import DisplayManager
  7. from ..template import Template
  8. console = Console()
  9. class VariableDisplayManager:
  10. """Handles all variable-related rendering.
  11. This manager is responsible for displaying variables, sections,
  12. and their values with appropriate formatting based on context.
  13. """
  14. def __init__(self, parent: "DisplayManager"):
  15. """Initialize VariableDisplayManager.
  16. Args:
  17. parent: Reference to parent DisplayManager for accessing shared resources
  18. """
  19. self.parent = parent
  20. def render_variable_value(
  21. self,
  22. variable,
  23. context: str = "default",
  24. is_dimmed: bool = False,
  25. var_satisfied: bool = True,
  26. ) -> str:
  27. """Render variable value with appropriate formatting based on context.
  28. Args:
  29. variable: Variable instance to render
  30. context: Display context ("default", "override", "disabled")
  31. is_dimmed: Whether the variable should be dimmed
  32. var_satisfied: Whether the variable's dependencies are satisfied
  33. Returns:
  34. Formatted string representation of the variable value
  35. """
  36. from . import IconManager
  37. # Handle disabled bool variables
  38. if (is_dimmed or not var_satisfied) and variable.type == "bool":
  39. if (
  40. hasattr(variable, "_original_disabled")
  41. and variable._original_disabled is not False
  42. ):
  43. return f"{variable._original_disabled} {IconManager.arrow_right()} False"
  44. return "False"
  45. # Handle config overrides with arrow
  46. if (
  47. variable.origin == "config"
  48. and hasattr(variable, "_original_stored")
  49. and variable.original_value != variable.value
  50. ):
  51. settings = self.parent.settings
  52. orig = self._format_value(variable, variable.original_value, max_length=settings.VALUE_MAX_LENGTH_SHORT)
  53. curr = variable.get_display_value(
  54. mask_sensitive=True, max_length=settings.VALUE_MAX_LENGTH_SHORT, show_none=False
  55. )
  56. if not curr:
  57. curr = str(variable.value) if variable.value else settings.TEXT_EMPTY_OVERRIDE
  58. return (
  59. f"{orig} [bold {settings.COLOR_WARNING}]{IconManager.arrow_right()} {curr}[/bold {settings.COLOR_WARNING}]"
  60. )
  61. # Default formatting
  62. settings = self.parent.settings
  63. value = variable.get_display_value(
  64. mask_sensitive=True, max_length=settings.VALUE_MAX_LENGTH_DEFAULT, show_none=True
  65. )
  66. if not variable.value:
  67. return f"[{settings.COLOR_MUTED}]{value}[/{settings.COLOR_MUTED}]"
  68. return value
  69. def _format_value(self, variable, value, max_length: int | None = None) -> str:
  70. """Helper to format a specific value.
  71. Args:
  72. variable: Variable instance
  73. value: Value to format
  74. max_length: Maximum length before truncation
  75. Returns:
  76. Formatted value string
  77. """
  78. settings = self.parent.settings
  79. if variable.sensitive:
  80. return settings.SENSITIVE_MASK
  81. if value is None or value == "":
  82. return f"[{settings.COLOR_MUTED}]({settings.TEXT_EMPTY_VALUE})[/{settings.COLOR_MUTED}]"
  83. val_str = str(value)
  84. return self.parent._truncate_value(val_str, max_length)
  85. def render_section(self, title: str, description: str | None) -> None:
  86. """Display a section header.
  87. Args:
  88. title: Section title
  89. description: Optional section description
  90. """
  91. settings = self.parent.settings
  92. if description:
  93. console.print(
  94. f"\n[{settings.STYLE_SECTION_TITLE}]{title}[/{settings.STYLE_SECTION_TITLE}] [{settings.STYLE_SECTION_DESC}]- {description}[/{settings.STYLE_SECTION_DESC}]"
  95. )
  96. else:
  97. console.print(f"\n[{settings.STYLE_SECTION_TITLE}]{title}[/{settings.STYLE_SECTION_TITLE}]")
  98. console.print(settings.SECTION_SEPARATOR_CHAR * settings.SECTION_SEPARATOR_LENGTH, style=settings.COLOR_MUTED)
  99. def _render_section_header(self, section, is_dimmed: bool, has_dependencies: bool) -> str:
  100. """Build section header text with appropriate styling.
  101. Args:
  102. section: VariableSection instance
  103. is_dimmed: Whether section is dimmed (disabled)
  104. has_dependencies: Whether section has dependency requirements
  105. Returns:
  106. Formatted header text with Rich markup
  107. """
  108. settings = self.parent.settings
  109. disabled_text = settings.LABEL_DISABLED if (is_dimmed and not has_dependencies) else ""
  110. if is_dimmed:
  111. required_part = " (required)" if section.required else ""
  112. return f"[bold {settings.STYLE_DISABLED}]{section.title}{required_part}{disabled_text}[/bold {settings.STYLE_DISABLED}]"
  113. else:
  114. required_text = settings.LABEL_REQUIRED if section.required else ""
  115. return f"[bold]{section.title}{required_text}{disabled_text}[/bold]"
  116. def _render_variable_row(self, var_name: str, variable, is_dimmed: bool, var_satisfied: bool) -> tuple:
  117. """Build variable row data for table display.
  118. Args:
  119. var_name: Variable name
  120. variable: Variable instance
  121. is_dimmed: Whether containing section is dimmed
  122. var_satisfied: Whether variable dependencies are satisfied
  123. Returns:
  124. Tuple of (var_display, type, default_val, description, row_style)
  125. """
  126. from . import IconManager
  127. settings = self.parent.settings
  128. # Build row style
  129. row_style = settings.STYLE_DISABLED if (is_dimmed or not var_satisfied) else None
  130. # Build default value
  131. default_val = self.render_variable_value(
  132. variable, is_dimmed=is_dimmed, var_satisfied=var_satisfied
  133. )
  134. # Build variable display name
  135. sensitive_icon = f" {IconManager.lock()}" if variable.sensitive else ""
  136. required_indicator = settings.LABEL_REQUIRED if variable.required else ""
  137. var_display = f"{settings.VAR_NAME_INDENT}{var_name}{sensitive_icon}{required_indicator}"
  138. return (
  139. var_display,
  140. variable.type or "str",
  141. default_val,
  142. variable.description or "",
  143. row_style,
  144. )
  145. def render_variables_table(self, template: "Template") -> None:
  146. """Display a table of variables for a template.
  147. All variables and sections are always shown. Disabled sections/variables
  148. are displayed with dimmed styling.
  149. Args:
  150. template: Template instance
  151. """
  152. if not (template.variables and template.variables.has_sections()):
  153. return
  154. settings = self.parent.settings
  155. console.print()
  156. console.print(f"[{settings.STYLE_HEADER}]Template Variables:[/{settings.STYLE_HEADER}]")
  157. variables_table = Table(show_header=True, header_style=settings.STYLE_TABLE_HEADER)
  158. variables_table.add_column("Variable", style=settings.STYLE_VAR_COL_NAME, no_wrap=True)
  159. variables_table.add_column("Type", style=settings.STYLE_VAR_COL_TYPE)
  160. variables_table.add_column("Default", style=settings.STYLE_VAR_COL_DEFAULT)
  161. variables_table.add_column("Description", style=settings.STYLE_VAR_COL_DESC)
  162. first_section = True
  163. for section in template.variables.get_sections().values():
  164. if not section.variables:
  165. continue
  166. if not first_section:
  167. variables_table.add_row("", "", "", "", style=settings.STYLE_DISABLED)
  168. first_section = False
  169. # Check if section is enabled AND dependencies are satisfied
  170. is_enabled = section.is_enabled()
  171. dependencies_satisfied = template.variables.is_section_satisfied(section.key)
  172. is_dimmed = not (is_enabled and dependencies_satisfied)
  173. has_dependencies = section.needs and len(section.needs) > 0
  174. # Render section header
  175. header_text = self._render_section_header(section, is_dimmed, has_dependencies)
  176. variables_table.add_row(header_text, "", "", "")
  177. # Render variables
  178. for var_name, variable in section.variables.items():
  179. # Skip toggle variable in required sections
  180. if section.required and section.toggle and var_name == section.toggle:
  181. continue
  182. # Check if variable's needs are satisfied
  183. var_satisfied = template.variables.is_variable_satisfied(var_name)
  184. # Build and add row
  185. row_data = self._render_variable_row(var_name, variable, is_dimmed, var_satisfied)
  186. variables_table.add_row(*row_data)
  187. console.print(variables_table)