|
| 1 | +import time |
1 | 2 | from enum import Enum |
2 | 3 | from typing import Optional |
3 | 4 |
|
@@ -80,6 +81,47 @@ class TUIComponents(str, Enum): |
80 | 81 | LOG_FILTER = "log-filter" |
81 | 82 |
|
82 | 83 |
|
| 84 | +class SubstateLine(Horizontal): |
| 85 | + """A single substate row with an attached timer.""" |
| 86 | + |
| 87 | + def __init__(self, text: str, indent: str, **kwargs): |
| 88 | + super().__init__(**kwargs) |
| 89 | + self.text = text |
| 90 | + self.indent = indent |
| 91 | + self.start_time = time.monotonic() |
| 92 | + self._line_widget: Static | None = None |
| 93 | + |
| 94 | + def compose(self): |
| 95 | + self._line_widget = Static(self._format_line(), classes="substate-line-text") |
| 96 | + yield self._line_widget |
| 97 | + |
| 98 | + def on_mount(self) -> None: |
| 99 | + self._refresh_timer() |
| 100 | + self.set_interval(1, self._refresh_timer) |
| 101 | + |
| 102 | + def _format_timer(self) -> str: |
| 103 | + elapsed = int(time.monotonic() - self.start_time) |
| 104 | + if elapsed < 60: |
| 105 | + return f"{elapsed}s" |
| 106 | + minutes = elapsed // 60 |
| 107 | + seconds = elapsed % 60 |
| 108 | + if minutes < 60: |
| 109 | + return f"{minutes}m {seconds}s" |
| 110 | + hours = minutes // 60 |
| 111 | + return f"{hours}h {minutes % 60}m" |
| 112 | + |
| 113 | + def _format_line(self) -> str: |
| 114 | + timer = self._format_timer() |
| 115 | + return f"{self.indent} └ {self.text} [#888888]({timer})[/#888888]" |
| 116 | + |
| 117 | + def _refresh_timer(self) -> None: |
| 118 | + try: |
| 119 | + if self._line_widget: |
| 120 | + self._line_widget.update(self._format_line()) |
| 121 | + except Exception: |
| 122 | + pass |
| 123 | + |
| 124 | + |
83 | 125 | class ProgressItem(Vertical): |
84 | 126 | """A vertical container for a status, description, and substates.""" |
85 | 127 |
|
@@ -176,7 +218,7 @@ async def _render_substates_recursive(self, container: Vertical, substates: list |
176 | 218 |
|
177 | 219 | for substate in substates: |
178 | 220 | # Render the current substate |
179 | | - substate_widget = Static(f"{indent} └ {substate.text}", classes="substate") |
| 221 | + substate_widget = SubstateLine(substate.text, indent, classes="substate-row") |
180 | 222 | await container.mount(substate_widget) |
181 | 223 |
|
182 | 224 | # Recursively render children if they exist |
|
0 commit comments