variable_display.py 9.4 KB

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