Repository: Textualize/textual
Pull request: improved eta
Improved ETA for progress bar

- Reduces refreshes of the ProgressBar. Previously the widget would repaint every time `update` was called. Even if the output remained the same.
- Improved calculation of ETA based on recent samples. ETA is extrapolated based on this speed.
- Added a `Clock` object to return relative time, and allow for easier mocking. This is public, but as yet undocumented. This might become a public interface if we want devs to be able to swap out algorithms.

Closes https://github.com/Textualize/textual/issues/4054

Problem statement:
Revised progress bar ETA
The ProgressBar "ETA" is quite naive. I *think* it assumes a constant speed, which is rarely the case for things you would want a progress bar for.

We need a smarter way of calculating the ETA which emphasizes more recent data points. There are a few ways of doing this. You could see the Rich progress bars for an examples, but there may also be better algorithms. Suggesting asking Google or ChatGPT.

The implementation could also use another look. It adds a Horizontal container, where the widget itself could have a horizontal layout. 

1d

Hints from issue discussion:
While making a start on this I ran into some confusing results and ended up uncovering #4096; so I think this will result in a revamp of how `ProgressBar` does the calculations even in the most simplistic of situations.

Some form of "please reset this `ProgressBar` for reuse" method might also be a good idea.
WiP on this has addressed #4096, but also uncovered a further issue where the `ETAStatus` appeared to be updating when it wasn't necessary; this has now been identified and fixed too.

An updated method of calculating the ETA is now in place and seems to produce reasonable results; for constant-time progress the timer looks spot on; and for accelerating, decelerating and reasonably random progress the results look good too, and generally more/less optimistic/pessimistic where appropriate.

Work on this is having further knock-on effects on how the documentation screen shots are generated, and also their use in snapshot tests; this will likely need some more work on how the docs for `ProgressBar` are handled, and might require removal of that code from snapshot tests. If the latter happens we'll need to do a bit of work on producing unit tests for the ETA calculation (which might require isolating the ETA code somewhat).

A question has also been raised about the newer approach taken to slide a window over the updates to perform the calculation.

+1d

Commit message: watch progress

Patch to review:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 14b45d4618..963f4eb491 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
   - Changed `Tabs`
   - Changed `TextArea`
   - Changed `Tree`
+- Improved ETA calculation for ProgressBar https://github.com/Textualize/textual/pull/4271
 
 ## [0.52.1] - 2024-02-20
 
diff --git a/docs/examples/widgets/progress_bar_isolated_.py b/docs/examples/widgets/progress_bar_isolated_.py
index 79907562cf..c1df28d5db 100644
--- a/docs/examples/widgets/progress_bar_isolated_.py
+++ b/docs/examples/widgets/progress_bar_isolated_.py
@@ -1,4 +1,5 @@
 from textual.app import App, ComposeResult
+from textual.clock import MockClock
 from textual.containers import Center, Middle
 from textual.timer import Timer
 from textual.widgets import Footer, ProgressBar
@@ -11,13 +12,14 @@ class IndeterminateProgressBar(App[None]):
     """Timer to simulate progress happening."""
 
     def compose(self) -> ComposeResult:
+        self.clock = MockClock()
         with Center():
             with Middle():
-                yield ProgressBar()
+                yield ProgressBar(clock=self.clock)
         yield Footer()
 
     def on_mount(self) -> None:
-        """Set up a timer to simulate progess happening."""
+        """Set up a timer to simulate progress happening."""
         self.progress_timer = self.set_interval(1 / 10, self.make_progress, pause=True)
 
     def make_progress(self) -> None:
@@ -31,14 +33,18 @@ def action_start(self) -> None:
 
     def key_f(self) -> None:
         # Freeze time for the indeterminate progress bar.
-        self.query_one(ProgressBar).query_one("#bar")._get_elapsed_time = lambda: 5
+        self.clock.set_time(5)
+        self.refresh()
 
     def key_t(self) -> None:
         # Freeze time to show always the same ETA.
-        self.query_one(ProgressBar).query_one("#eta")._get_elapsed_time = lambda: 3.9
-        self.query_one(ProgressBar).update(total=100, progress=39)
+        self.clock.set_time(0)
+        self.query_one(ProgressBar).update(total=100, progress=0)
+        self.clock.set_time(3.9)
+        self.query_one(ProgressBar).update(progress=39)
 
     def key_u(self) -> None:
+        self.refresh()
         self.query_one(ProgressBar).update(total=100, progress=100)
 
 
diff --git a/docs/examples/widgets/progress_bar_styled.py b/docs/examples/widgets/progress_bar_styled.py
index 96c5005bab..f5b95ad055 100644
--- a/docs/examples/widgets/progress_bar_styled.py
+++ b/docs/examples/widgets/progress_bar_styled.py
@@ -18,7 +18,7 @@ def compose(self) -> ComposeResult:
         yield Footer()
 
     def on_mount(self) -> None:
-        """Set up a timer to simulate progess happening."""
+        """Set up a timer to simulate progress happening."""
         self.progress_timer = self.set_interval(1 / 10, self.make_progress, pause=True)
 
     def make_progress(self) -> None:
diff --git a/docs/examples/widgets/progress_bar_styled_.py b/docs/examples/widgets/progress_bar_styled_.py
index 8428f359a1..b195327567 100644
--- a/docs/examples/widgets/progress_bar_styled_.py
+++ b/docs/examples/widgets/progress_bar_styled_.py
@@ -1,4 +1,5 @@
 from textual.app import App, ComposeResult
+from textual.clock import MockClock
 from textual.containers import Center, Middle
 from textual.timer import Timer
 from textual.widgets import Footer, ProgressBar
@@ -12,13 +13,14 @@ class StyledProgressBar(App[None]):
     """Timer to simulate progress happening."""
 
     def compose(self) -> ComposeResult:
+        self.clock = MockClock()
         with Center():
             with Middle():
-                yield ProgressBar()
+                yield ProgressBar(clock=self.clock)
         yield Footer()
 
     def on_mount(self) -> None:
-        """Set up a timer to simulate progess happening."""
+        """Set up a timer to simulate progress happening."""
         self.progress_timer = self.set_interval(1 / 10, self.make_progress, pause=True)
 
     def make_progress(self) -> None:
@@ -29,15 +31,18 @@ def action_start(self) -> None:
         """Start the progress tracking."""
         self.query_one(ProgressBar).update(total=100)
         self.progress_timer.resume()
+        self.query_one(ProgressBar).refresh()
 
     def key_f(self) -> None:
         # Freeze time for the indeterminate progress bar.
-        self.query_one(ProgressBar).query_one("#bar")._get_elapsed_time = lambda: 5
+        self.clock.set_time(5.0)
 
     def key_t(self) -> None:
         # Freeze time to show always the same ETA.
-        self.query_one(ProgressBar).query_one("#eta")._get_elapsed_time = lambda: 3.9
-        self.query_one(ProgressBar).update(total=100, progress=39)
+        self.clock.set_time(0)
+        self.query_one(ProgressBar).update(total=100, progress=0)
+        self.clock.set_time(3.9)
+        self.query_one(ProgressBar).update(progress=39)
 
     def key_u(self) -> None:
         self.query_one(ProgressBar).update(total=100, progress=100)
diff --git a/src/textual/clock.py b/src/textual/clock.py
new file mode 100644
index 0000000000..6df651bcc9
--- /dev/null
+++ b/src/textual/clock.py
@@ -0,0 +1,74 @@
+from __future__ import annotations
+
+from time import monotonic
+from typing import Callable
+
+import rich.repr
+
+
+@rich.repr.auto(angular=True)
+class Clock:
+    """An object to get relative time.
+
+    The `time` attribute of clock will return the time in seconds since the
+    Clock was created or reset.
+
+    """
+
+    def __init__(self, *, get_time: Callable[[], float] = monotonic) -> None:
+        """Create a clock.
+
+        Args:
+            get_time: A callable to get time in seconds.
+            start: Start the clock (time is 0 unless clock has been started).
+        """
+        self._get_time = get_time
+        self._start_time = self._get_time()
+
+    def __rich_repr__(self) -> rich.repr.Result:
+        yield self.time
+
+    def clone(self) -> Clock:
+        """Clone the Clock with an independent time."""
+        return Clock(get_time=self._get_time)
+
+    def reset(self) -> None:
+        """Reset the clock."""
+        self._start_time = self._get_time()
+
+    @property
+    def time(self) -> float:
+        """Time since creation or reset."""
+        return self._get_time() - self._start_time
+
+
+class MockClock(Clock):
+    """A mock clock object where the time may be explicitly set."""
+
+    def __init__(self, time: float = 0.0) -> None:
+        """Construct a mock clock."""
+        self._time = time
+        super().__init__(get_time=lambda: self._time)
+
+    def clone(self) -> MockClock:
+        """Clone the mocked clock (clone will return the same time as original)."""
+        clock = MockClock(self._time)
+        clock._get_time = self._get_time
+        clock._time = self._time
+        return clock
+
+    def reset(self) -> None:
+        """A null-op because it doesn't make sense to reset a mocked clock."""
+
+    def set_time(self, time: float) -> None:
+        """Set the time for the clock.
+
+        Args:
+            time: Time to set.
+        """
+        self._time = time
+
+    @property
+    def time(self) -> float:
+        """Time since creation or reset."""
+        return self._get_time()
diff --git a/src/textual/dom.py b/src/textual/dom.py
index 94980c87b0..2b47607db4 100644
--- a/src/textual/dom.py
+++ b/src/textual/dom.py
@@ -474,13 +474,11 @@ def __init_subclass__(
         cls._merged_bindings = cls._merge_bindings()
         cls._css_type_names = frozenset(css_type_names)
         cls._computes = frozenset(
-            dict.fromkeys(
-                [
-                    name.lstrip("_")[8:]
-                    for name in dir(cls)
-                    if name.startswith(("_compute_", "compute_"))
-                ]
-            ).keys()
+            [
+                name.lstrip("_")[8:]
+                for name in dir(cls)
+                if name.startswith(("_compute_", "compute_"))
+            ]
         )
 
     def get_component_styles(self, name: str) -> RenderStyles:
diff --git a/src/textual/eta.py b/src/textual/eta.py
new file mode 100644
index 0000000000..ffaaa1f0a7
--- /dev/null
+++ b/src/textual/eta.py
@@ -0,0 +1,126 @@
+from __future__ import annotations
+
+import bisect
+from math import ceil
+from time import monotonic
+
+import rich.repr
+
+
+@rich.repr.auto(angular=True)
+class ETA:
+    """Calculate speed and estimate time to arrival."""
+
+    def __init__(
+        self, estimation_period: float = 60, extrapolate_period: float = 30
+    ) -> None:
+        """Create an ETA.
+
+        Args:
+            estimation_period: Period in seconds, used to calculate speed.
+            extrapolate_period: Maximum number of seconds used to estimate progress after last sample.
+        """
+        self.estimation_period = estimation_period
+        self.max_extrapolate = extrapolate_period
+        self._samples: list[tuple[float, float]] = [(0.0, 0.0)]
+        self._add_count = 0
+
+    def __rich_repr__(self) -> rich.repr.Result:
+        yield "speed", self.speed
+        yield "eta", self.get_eta(monotonic())
+
+    @property
+    def first_sample(self) -> tuple[float, float]:
+        """First sample, or `None` if no samples."""
+        assert self._samples, "Assumes samples not empty"
+        return self._samples[0]
+
+    @property
+    def last_sample(self) -> tuple[float, float]:
+        """Last sample, or `None` if no samples."""
+        assert self._samples, "Assumes samples not empty"
+        return self._samples[-1]
+
+    def reset(self) -> None:
+        """Start ETA calculations from current time."""
+        del self._samples[:]
+
+    def add_sample(self, time: float, progress: float) -> None:
+        """Add a new sample.
+
+        Args:
+            time: Time when sample occurred.
+            progress: Progress ratio (0 is start, 1 is complete).
+        """
+        if self._samples and self.last_sample[1] > progress:
+            # If progress goes backwards, we need to reset calculations
+            self.reset()
+        self._samples.append((time, progress))
+        self._add_count += 1
+        if self._add_count % 100 == 0:
+            # Prune periodically so we don't accumulate vast amounts of samples
+            self._prune()
+
+    def _prune(self) -> None:
+        """Prune old samples."""
+        if len(self._samples) <= 10:
+            # Keep at least 10 samples
+            return
+        prune_time = self._samples[-1][0] - self.estimation_period
+        index = bisect.bisect_left(self._samples, (prune_time, 0))
+        del self._samples[:index]
+
+    def _get_progress_at(self, time: float) -> tuple[float, float]:
+        """Get the progress at a specific time."""
+
+        index = bisect.bisect_left(self._samples, (time, 0))
+        if index >= len(self._samples):
+            return self.last_sample
+        if index == 0:
+            return self.first_sample
+        # Linearly interpolate progress between two samples
+        time1, progress1 = self._samples[index - 1]
+        time2, progress2 = self._samples[index]
+        factor = (time - time1) / (time2 - time1)
+        intermediate_progress = progress1 + (progress2 - progress1) * factor
+        return time, intermediate_progress
+
+    @property
+    def speed(self) -> float | None:
+        """The current speed, or `None` if it couldn't be calculated."""
+
+        if len(self._samples) < 2:
+            # Need at least 2 samples to calculate speed
+            return None
+
+        recent_sample_time, progress2 = self.last_sample
+        progress_start_time, progress1 = self._get_progress_at(
+            recent_sample_time - self.estimation_period
+        )
+        time_delta = recent_sample_time - progress_start_time
+        distance = progress2 - progress1
+        speed = distance / time_delta if time_delta else 0
+        return speed
+
+    def get_eta(self, time: float) -> int | None:
+        """Estimated seconds until completion, or `None` if no estimate can be made.
+
+        Args:
+            time: Current time.
+        """
+        speed = self.speed
+        if not speed:
+            # Not enough samples to guess
+            return None
+        recent_time, recent_progress = self.last_sample
+        remaining = 1.0 - recent_progress
+        if remaining <= 0:
+            # Complete
+            return 0
+        # The bar is not complete, so we will extrapolate progress
+        # This will give us a countdown, even with no samples
+        time_since_sample = min(self.max_extrapolate, time - recent_time)
+        extrapolate_progress = speed * time_since_sample
+        # We don't want to extrapolate all the way to 0, as that would erroneously suggest it is finished
+        eta = max(1.0, (remaining - extrapolate_progress) / speed)
+        return ceil(eta)
diff --git a/src/textual/widgets/_progress_bar.py b/src/textual/widgets/_progress_bar.py
index fea9c2fba2..007188ad56 100644
--- a/src/textual/widgets/_progress_bar.py
+++ b/src/textual/widgets/_progress_bar.py
@@ -2,18 +2,17 @@
 
 from __future__ import annotations
 
-from math import ceil
-from time import monotonic
-from typing import Callable, Optional
+from typing import Optional
 
 from rich.style import Style
 
 from .._types import UnusedParameter
 from ..app import ComposeResult, RenderResult
+from ..clock import Clock
+from ..eta import ETA
 from ..geometry import clamp
 from ..reactive import reactive
 from ..renderables.bar import Bar as BarRenderable
-from ..timer import Timer
 from ..widget import Widget
 from ..widgets import Label
 
@@ -58,10 +57,8 @@ class Bar(Widget, can_focus=False):
     }
     """
 
-    _percentage: reactive[float | None] = reactive[Optional[float]](None)
+    percentage: reactive[float | None] = reactive[Optional[float]](None)
     """The percentage of progress that has been completed."""
-    _start_time: float | None
-    """The time when the widget started tracking progress."""
 
     def __init__(
         self,
@@ -69,13 +66,22 @@ def __init__(
         id: str | None = None,
         classes: str | None = None,
         disabled: bool = False,
+        clock: Clock | None = None,
     ):
         """Create a bar for a [`ProgressBar`][textual.widgets.ProgressBar]."""
+        self._clock = (clock or Clock()).clone()
         super().__init__(name=name, id=id, classes=classes, disabled=disabled)
-        self._start_time = None
-        self._percentage = None
 
-    def watch__percentage(self, percentage: float | None) -> None:
+    def _validate_percentage(self, percentage: float | None) -> float | None:
+        """Avoid updating the bar, if the percentage increase is too small to render."""
+        width = self.size.width * 2
+        return (
+            None
+            if percentage is None
+            else (int(percentage * width) / width if width else percentage)
+        )
+
+    def watch_percentage(self, percentage: float | None) -> None:
         """Manage the timer that enables the indeterminate bar animation."""
         if percentage is not None:
             self.auto_refresh = None
@@ -84,16 +90,16 @@ def watch__percentage(self, percentage: float | None) -> None:
 
     def render(self) -> RenderResult:
         """Render the bar with the correct portion filled."""
-        if self._percentage is None:
+        if self.percentage is None:
             return self.render_indeterminate()
         else:
             bar_style = (
                 self.get_component_rich_style("bar--bar")
-                if self._percentage < 1
+                if self.percentage < 1
                 else self.get_component_rich_style("bar--complete")
             )
             return BarRenderable(
-                highlight_range=(0, self.size.width * self._percentage),
+                highlight_range=(0, self.size.width * self.percentage),
                 highlight_style=Style.from_color(bar_style.color),
                 background_style=Style.from_color(bar_style.bgcolor),
             )
@@ -104,14 +110,15 @@ def render_indeterminate(self) -> RenderResult:
         highlighted_bar_width = 0.25 * width
         # Width used to enable the visual effect of the bar going into the corners.
         total_imaginary_width = width + highlighted_bar_width
-
+        start: float
+        end: float
         if self.app.animation_level == "none":
             start = 0
             end = width
         else:
             speed = 30  # Cells per second.
             # Compute the position of the bar.
-            start = (speed * self._get_elapsed_time()) % (2 * total_imaginary_width)
+            start = (speed * self._clock.time) % (2 * total_imaginary_width)
             if start > total_imaginary_width:
                 # If the bar is to the right of its width, wrap it back from right to left.
                 start = 2 * total_imaginary_width - start  # = (tiw - (start - tiw))
@@ -125,21 +132,6 @@ def render_indeterminate(self) -> RenderResult:
             background_style=Style.from_color(bar_style.bgcolor),
         )
 
-    def _get_elapsed_time(self) -> float:
-        """Get time for the indeterminate progress animation.
-
-        This method ensures that the progress bar animation always starts at the
-        beginning and it also makes it easier to test the bar if we monkey patch
-        this method.
-
-        Returns:
-            The time elapsed since the bar started being animated.
-        """
-        if self._start_time is None:
-            self._start_time = monotonic()
-            return 0
-        return monotonic() - self._start_time
-
 
 class PercentageStatus(Label):
     """A label to display the percentage status of the progress bar."""
@@ -151,32 +143,14 @@ class PercentageStatus(Label):
     }
     """
 
-    _label_text: reactive[str] = reactive("", repaint=False)
-    """This is used as an auxiliary reactive to only refresh the label when needed."""
-    _percentage: reactive[float | None] = reactive[Optional[float]](None)
+    percentage: reactive[int | None] = reactive[Optional[int]](None)
     """The percentage of progress that has been completed."""
 
-    def __init__(
-        self,
-        name: str | None = None,
-        id: str | None = None,
-        classes: str | None = None,
-        disabled: bool = False,
-    ):
-        super().__init__(name=name, id=id, classes=classes, disabled=disabled)
-        self._percentage = None
-        self._label_text = "--%"
-
-    def watch__percentage(self, percentage: float | None) -> None:
-        """Manage the text that shows the percentage of progress."""
-        if percentage is None:
-            self._label_text = "--%"
-        else:
-            self._label_text = f"{int(100 * percentage)}%"
+    def _validate_percentage(self, percentage: float | None) -> int | None:
+        return None if percentage is None else round(percentage * 100)
 
-    def watch__label_text(self, label_text: str) -> None:
-        """If the label text changed, update the renderable (which also refreshes)."""
-        self.update(label_text)
+    def render(self) -> RenderResult:
+        return "--%" if self.percentage is None else f"{self.percentage}%"
 
 
 class ETAStatus(Label):
@@ -188,79 +162,23 @@ class ETAStatus(Label):
         content-align-horizontal: right;
     }
     """
+    eta: reactive[float | None] = reactive[Optional[float]](None)
+    """Estimated number of seconds till completion."""
 
-    _label_text: reactive[str] = reactive("", repaint=False)
-    """This is used as an auxiliary reactive to only refresh the label when needed."""
-    _percentage: reactive[float | None] = reactive[Optional[float]](None)
-    """The percentage of progress that has been completed."""
-    _refresh_timer: Timer | None
-    """Timer to update ETA status even when progress stalls."""
-    _start_time: float | None
-    """The time when the widget started tracking progress."""
-
-    def __init__(
-        self,
-        name: str | None = None,
-        id: str | None = None,
-        classes: str | None = None,
-        disabled: bool = False,
-    ):
-        super().__init__(name=name, id=id, classes=classes, disabled=disabled)
-        self._percentage = None
-        self._label_text = "--:--:--"
-        self._start_time = None
-        self._refresh_timer = None
-
-    def on_mount(self) -> None:
-        """Periodically refresh the countdown so that the ETA is always up to date."""
-        self._refresh_timer = self.set_interval(1 / 2, self.update_eta, pause=True)
-
-    def watch__percentage(self, percentage: float | None) -> None:
-        if percentage is None:
-            self._label_text = "--:--:--"
-        else:
-            if self._refresh_timer is not None:
-                self._refresh_timer.reset()
-            self.update_eta()
-
-    def update_eta(self) -> None:
-        """Update the ETA display."""
-        percentage = self._percentage
-        delta = self._get_elapsed_time()
-        # We display --:--:-- if we haven't started, if we are done,
-        # or if we don't know when we started keeping track of time.
-        if not percentage or percentage >= 1 or not delta:
-            self._label_text = "--:--:--"
-            # If we are done, we can delete the timer that periodically refreshes
-            # the countdown display.
-            if percentage is not None and percentage >= 1:
-                self.auto_refresh = None
-        # Render a countdown timer with hh:mm:ss, unless it's a LONG time.
+    def render(self) -> RenderResult:
+        """Render the ETA display."""
+        eta = self.eta
+        if eta is None:
+            return "--:--:--"
         else:
-            left = ceil((delta / percentage) * (1 - percentage))
-            minutes, seconds = divmod(left, 60)
+            minutes, seconds = divmod(round(eta), 60)
             hours, minutes = divmod(minutes, 60)
             if hours > 999999:
-                self._label_text = "+999999h"
+                return "+999999h"
             elif hours > 99:
-                self._label_text = f"{hours}h"
+                return f"{hours}h"
             else:
-                self._label_text = f"{hours:02}:{minutes:02}:{seconds:02}"
-
-    def _get_elapsed_time(self) -> float:
-        """Get time to estimate time to progress completion.
-
-        Returns:
-            The time elapsed since the bar started being animated.
-        """
-        if self._start_time is None:
-            self._start_time = monotonic()
-            return 0
-        return monotonic() - self._start_time
-
-    def watch__label_text(self, label_text: str) -> None:
-        """If the ETA label changed, update the renderable (which also refreshes)."""
-        self.update(label_text)
+                return f"{hours:02}:{minutes:02}:{seconds:02}"
 
 
 class ProgressBar(Widget, can_focus=False):
@@ -296,6 +214,7 @@ class ProgressBar(Widget, can_focus=False):
         print(progress_bar.percentage)  # 0.5
         ```
     """
+    _display_eta: reactive[int | None] = reactive[Optional[int]](None)
 
     def __init__(
         self,
@@ -308,6 +227,7 @@ def __init__(
         id: str | None = None,
         classes: str | None = None,
         disabled: bool = False,
+        clock: Clock | None = None,
     ):
         """Create a Progress Bar widget.
 
@@ -332,47 +252,28 @@ def key_space(self):
             id: The ID of the widget in the DOM.
             classes: The CSS classes for the widget.
             disabled: Whether the widget is disabled or not.
+            clock: An optional clock object (leave as default unless testing).
         """
         super().__init__(name=name, id=id, classes=classes, disabled=disabled)
+        self.total = total
         self.show_bar = show_bar
         self.show_percentage = show_percentage
         self.show_eta = show_eta
+        self._clock = clock or Clock()
+        self._eta = ETA()
 
-        self.total = total
+    def on_mount(self) -> None:
+        self.update()
+        self.set_interval(0.5, self.update)
+        self._clock.reset()
 
     def compose(self) -> ComposeResult:
-        # We create a closure so that we can determine what are the sub-widgets
-        # that are present and, therefore, will need to be notified about changes
-        # to the percentage.
-        def update_percentage(
-            widget: Bar | PercentageStatus | ETAStatus,
-        ) -> Callable[[float | None], None]:
-            """Closure to allow updating the percentage of a given widget."""
-
-            def updater(percentage: float | None) -> None:
-                """Update the percentage reactive of the enclosed widget."""
-                widget._percentage = percentage
-
-            return updater
-
         if self.show_bar:
-            bar = Bar(id="bar")
-            self.watch(self, "percentage", update_percentage(bar))
-            yield bar
+            yield Bar(id="bar", clock=self._clock).data_bind(ProgressBar.percentage)
         if self.show_percentage:
-            percentage_status = PercentageStatus(id="percentage")
-            self.watch(self, "percentage", update_percentage(percentage_status))
-            yield percentage_status
+            yield PercentageStatus(id="percentage").data_bind(ProgressBar.percentage)
         if self.show_eta:
-            eta_status = ETAStatus(id="eta")
-            self.watch(self, "percentage", update_percentage(eta_status))
-            yield eta_status
-
-    def _validate_progress(self, progress: float) -> float:
-        """Clamp the progress between 0 and the maximum total."""
-        if self.total is not None:
-            return clamp(progress, 0, self.total)
-        return progress
+            yield ETAStatus(id="eta").data_bind(eta=ProgressBar._display_eta)
 
     def _validate_total(self, total: float | None) -> float | None:
         """Ensure the total is not negative."""
@@ -380,21 +281,21 @@ def _validate_total(self, total: float | None) -> float | None:
             return total
         return max(0, total)
 
-    def _watch_total(self) -> None:
-        """Re-validate progress."""
-        self.progress = self.progress
-
     def _compute_percentage(self) -> float | None:
         """Keep the percentage of progress updated automatically.
 
         This will report a percentage of `1` if the total is zero.
         """
         if self.total:
-            return self.progress / self.total
+            return clamp(self.progress / self.total, 0.0, 1.0)
         elif self.total == 0:
-            return 1
+            return 1.0
         return None
 
+    def _watch_progress(self, progress: float) -> None:
+        """Perform update when progress is modified."""
+        self.update(progress=progress)
+
     def advance(self, advance: float = 1) -> None:
         """Advance the progress of the progress bar by the given amount.
 
@@ -406,7 +307,7 @@ def advance(self, advance: float = 1) -> None:
         Args:
             advance: Number of steps to advance progress by.
         """
-        self.progress += advance
+        self.update(advance=advance)
 
     def update(
         self,
@@ -430,9 +331,23 @@ def update(
             progress: Set the progress to the given number of steps.
             advance: Advance the progress by this number of steps.
         """
+        current_time = self._clock.time
         if not isinstance(total, UnusedParameter):
+            if total != self.total:
+                self._eta.reset()
             self.total = total
+
+        def add_sample() -> None:
+            """Add a new sample."""
+            if self.progress is not None and self.total:
+                self._eta.add_sample(current_time, self.progress / self.total)
+
         if not isinstance(progress, UnusedParameter):
             self.progress = progress
+            add_sample()
+
         if not isinstance(advance, UnusedParameter):
             self.progress += advance
+            add_sample()
+
+        self._display_eta = self._eta.get_eta(current_time)
diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
index 04502cde5c..be506192ad 100644
--- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
+++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
@@ -28648,136 +28648,136 @@
           font-weight: 700;
       }
   
-      .terminal-2764447286-matrix {
+      .terminal-2114723073-matrix {
           font-family: Fira Code, monospace;
           font-size: 20px;
           line-height: 24.4px;
           font-variant-east-asian: full-width;
       }
   
-      .terminal-2764447286-title {
+      .terminal-2114723073-title {
           font-size: 18px;
           font-weight: bold;
           font-family: arial;
       }
   
-      .terminal-2764447286-r1 { fill: #e1e1e1 }
-  .terminal-2764447286-r2 { fill: #c5c8c6 }
-  .terminal-2764447286-r3 { fill: #fea62b }
-  .terminal-2764447286-r4 { fill: #323232 }
-  .terminal-2764447286-r5 { fill: #dde8f3;font-weight: bold }
-  .terminal-2764447286-r6 { fill: #ddedf9 }
+      .terminal-2114723073-r1 { fill: #e1e1e1 }
+  .terminal-2114723073-r2 { fill: #c5c8c6 }
+  .terminal-2114723073-r3 { fill: #fea62b }
+  .terminal-2114723073-r4 { fill: #323232 }
+  .terminal-2114723073-r5 { fill: #dde8f3;font-weight: bold }
+  .terminal-2114723073-r6 { fill: #ddedf9 }
       </style>
   
       <defs>
-      <clipPath id="terminal-2764447286-clip-terminal">
+      <clipPath id="terminal-2114723073-clip-terminal">
         <rect x="0" y="0" width="975.0" height="584.5999999999999" />
       </clipPath>
-      <clipPath id="terminal-2764447286-line-0">
+      <clipPath id="terminal-2114723073-line-0">
       <rect x="0" y="1.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-1">
+  <clipPath id="terminal-2114723073-line-1">
       <rect x="0" y="25.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-2">
+  <clipPath id="terminal-2114723073-line-2">
       <rect x="0" y="50.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-3">
+  <clipPath id="terminal-2114723073-line-3">
       <rect x="0" y="74.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-4">
+  <clipPath id="terminal-2114723073-line-4">
       <rect x="0" y="99.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-5">
+  <clipPath id="terminal-2114723073-line-5">
       <rect x="0" y="123.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-6">
+  <clipPath id="terminal-2114723073-line-6">
       <rect x="0" y="147.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-7">
+  <clipPath id="terminal-2114723073-line-7">
       <rect x="0" y="172.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-8">
+  <clipPath id="terminal-2114723073-line-8">
       <rect x="0" y="196.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-9">
+  <clipPath id="terminal-2114723073-line-9">
       <rect x="0" y="221.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-10">
+  <clipPath id="terminal-2114723073-line-10">
       <rect x="0" y="245.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-11">
+  <clipPath id="terminal-2114723073-line-11">
       <rect x="0" y="269.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-12">
+  <clipPath id="terminal-2114723073-line-12">
       <rect x="0" y="294.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-13">
+  <clipPath id="terminal-2114723073-line-13">
       <rect x="0" y="318.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-14">
+  <clipPath id="terminal-2114723073-line-14">
       <rect x="0" y="343.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-15">
+  <clipPath id="terminal-2114723073-line-15">
       <rect x="0" y="367.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-16">
+  <clipPath id="terminal-2114723073-line-16">
       <rect x="0" y="391.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-17">
+  <clipPath id="terminal-2114723073-line-17">
       <rect x="0" y="416.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-18">
+  <clipPath id="terminal-2114723073-line-18">
       <rect x="0" y="440.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-19">
+  <clipPath id="terminal-2114723073-line-19">
       <rect x="0" y="465.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-20">
+  <clipPath id="terminal-2114723073-line-20">
       <rect x="0" y="489.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-21">
+  <clipPath id="terminal-2114723073-line-21">
       <rect x="0" y="513.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-2764447286-line-22">
+  <clipPath id="terminal-2114723073-line-22">
       <rect x="0" y="538.3" width="976" height="24.65"/>
               </clipPath>
       </defs>
   
-      <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/><text class="terminal-2764447286-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">IndeterminateProgressBar</text>
+      <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/><text class="terminal-2114723073-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">IndeterminateProgressBar</text>
               <g transform="translate(26,22)">
               <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
               <circle cx="22" cy="0" r="7" fill="#febc2e"/>
               <circle cx="44" cy="0" r="7" fill="#28c840"/>
               </g>
           
-      <g transform="translate(9, 41)" clip-path="url(#terminal-2764447286-clip-terminal)">
+      <g transform="translate(9, 41)" clip-path="url(#terminal-2114723073-clip-terminal)">
       <rect fill="#1e1e1e" x="0" y="1.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="1.5" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="1.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="50.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="50.3" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="50.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="74.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="74.7" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="74.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="99.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="99.1" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="99.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="123.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="123.5" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="123.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="147.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="147.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="172.3" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="172.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="196.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="196.7" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="196.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="221.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="221.1" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="221.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="245.5" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="245.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="269.9" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="366" y="269.9" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="597.8" y="269.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="622.2" y="269.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="658.8" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="671" y="269.9" width="97.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="269.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="294.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="294.3" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="294.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="318.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="318.7" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="318.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="343.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="343.1" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="343.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="367.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="367.5" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="367.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="391.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="391.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="391.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="416.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="416.3" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="416.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="440.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="440.7" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="440.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="465.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="465.1" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="465.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="489.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="489.5" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="489.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="513.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="513.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="513.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="538.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="538.3" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="538.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#0053aa" x="0" y="562.7" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#0178d4" x="36.6" y="562.7" width="85.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#0178d4" x="122" y="562.7" width="854" height="24.65" shape-rendering="crispEdges"/>
-      <g class="terminal-2764447286-matrix">
-      <text class="terminal-2764447286-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2764447286-line-0)">
-  </text><text class="terminal-2764447286-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2764447286-line-1)">
-  </text><text class="terminal-2764447286-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2764447286-line-2)">
-  </text><text class="terminal-2764447286-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2764447286-line-3)">
-  </text><text class="terminal-2764447286-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2764447286-line-4)">
-  </text><text class="terminal-2764447286-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2764447286-line-5)">
-  </text><text class="terminal-2764447286-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2764447286-line-6)">
-  </text><text class="terminal-2764447286-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2764447286-line-7)">
-  </text><text class="terminal-2764447286-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2764447286-line-8)">
-  </text><text class="terminal-2764447286-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-2764447286-line-9)">
-  </text><text class="terminal-2764447286-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-2764447286-line-10)">
-  </text><text class="terminal-2764447286-r3" x="207.4" y="288.4" textLength="146.4" clip-path="url(#terminal-2764447286-line-11)">━━━━━━━━━━━━</text><text class="terminal-2764447286-r3" x="353.8" y="288.4" textLength="12.2" clip-path="url(#terminal-2764447286-line-11)">╸</text><text class="terminal-2764447286-r4" x="366" y="288.4" textLength="231.8" clip-path="url(#terminal-2764447286-line-11)">━━━━━━━━━━━━━━━━━━━</text><text class="terminal-2764447286-r1" x="622.2" y="288.4" textLength="36.6" clip-path="url(#terminal-2764447286-line-11)">39%</text><text class="terminal-2764447286-r1" x="671" y="288.4" textLength="97.6" clip-path="url(#terminal-2764447286-line-11)">00:00:07</text><text class="terminal-2764447286-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-2764447286-line-11)">
-  </text><text class="terminal-2764447286-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-2764447286-line-12)">
-  </text><text class="terminal-2764447286-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-2764447286-line-13)">
-  </text><text class="terminal-2764447286-r2" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-2764447286-line-14)">
-  </text><text class="terminal-2764447286-r2" x="976" y="386" textLength="12.2" clip-path="url(#terminal-2764447286-line-15)">
-  </text><text class="terminal-2764447286-r2" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-2764447286-line-16)">
-  </text><text class="terminal-2764447286-r2" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-2764447286-line-17)">
-  </text><text class="terminal-2764447286-r2" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-2764447286-line-18)">
-  </text><text class="terminal-2764447286-r2" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-2764447286-line-19)">
-  </text><text class="terminal-2764447286-r2" x="976" y="508" textLength="12.2" clip-path="url(#terminal-2764447286-line-20)">
-  </text><text class="terminal-2764447286-r2" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-2764447286-line-21)">
-  </text><text class="terminal-2764447286-r2" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-2764447286-line-22)">
-  </text><text class="terminal-2764447286-r5" x="0" y="581.2" textLength="36.6" clip-path="url(#terminal-2764447286-line-23)">&#160;S&#160;</text><text class="terminal-2764447286-r6" x="36.6" y="581.2" textLength="85.4" clip-path="url(#terminal-2764447286-line-23)">&#160;Start&#160;</text>
+      <g class="terminal-2114723073-matrix">
+      <text class="terminal-2114723073-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2114723073-line-0)">
+  </text><text class="terminal-2114723073-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2114723073-line-1)">
+  </text><text class="terminal-2114723073-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2114723073-line-2)">
+  </text><text class="terminal-2114723073-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2114723073-line-3)">
+  </text><text class="terminal-2114723073-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2114723073-line-4)">
+  </text><text class="terminal-2114723073-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2114723073-line-5)">
+  </text><text class="terminal-2114723073-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2114723073-line-6)">
+  </text><text class="terminal-2114723073-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2114723073-line-7)">
+  </text><text class="terminal-2114723073-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2114723073-line-8)">
+  </text><text class="terminal-2114723073-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-2114723073-line-9)">
+  </text><text class="terminal-2114723073-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-2114723073-line-10)">
+  </text><text class="terminal-2114723073-r3" x="207.4" y="288.4" textLength="146.4" clip-path="url(#terminal-2114723073-line-11)">━━━━━━━━━━━━</text><text class="terminal-2114723073-r4" x="353.8" y="288.4" textLength="12.2" clip-path="url(#terminal-2114723073-line-11)">╺</text><text class="terminal-2114723073-r4" x="366" y="288.4" textLength="231.8" clip-path="url(#terminal-2114723073-line-11)">━━━━━━━━━━━━━━━━━━━</text><text class="terminal-2114723073-r1" x="622.2" y="288.4" textLength="36.6" clip-path="url(#terminal-2114723073-line-11)">39%</text><text class="terminal-2114723073-r1" x="671" y="288.4" textLength="97.6" clip-path="url(#terminal-2114723073-line-11)">00:00:07</text><text class="terminal-2114723073-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-2114723073-line-11)">
+  </text><text class="terminal-2114723073-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-2114723073-line-12)">
+  </text><text class="terminal-2114723073-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-2114723073-line-13)">
+  </text><text class="terminal-2114723073-r2" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-2114723073-line-14)">
+  </text><text class="terminal-2114723073-r2" x="976" y="386" textLength="12.2" clip-path="url(#terminal-2114723073-line-15)">
+  </text><text class="terminal-2114723073-r2" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-2114723073-line-16)">
+  </text><text class="terminal-2114723073-r2" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-2114723073-line-17)">
+  </text><text class="terminal-2114723073-r2" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-2114723073-line-18)">
+  </text><text class="terminal-2114723073-r2" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-2114723073-line-19)">
+  </text><text class="terminal-2114723073-r2" x="976" y="508" textLength="12.2" clip-path="url(#terminal-2114723073-line-20)">
+  </text><text class="terminal-2114723073-r2" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-2114723073-line-21)">
+  </text><text class="terminal-2114723073-r2" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-2114723073-line-22)">
+  </text><text class="terminal-2114723073-r5" x="0" y="581.2" textLength="36.6" clip-path="url(#terminal-2114723073-line-23)">&#160;S&#160;</text><text class="terminal-2114723073-r6" x="36.6" y="581.2" textLength="85.4" clip-path="url(#terminal-2114723073-line-23)">&#160;Start&#160;</text>
       </g>
       </g>
   </svg>
@@ -28807,138 +28807,138 @@
           font-weight: 700;
       }
   
-      .terminal-3956614203-matrix {
+      .terminal-1351164996-matrix {
           font-family: Fira Code, monospace;
           font-size: 20px;
           line-height: 24.4px;
           font-variant-east-asian: full-width;
       }
   
-      .terminal-3956614203-title {
+      .terminal-1351164996-title {
           font-size: 18px;
           font-weight: bold;
           font-family: arial;
       }
   
-      .terminal-3956614203-r1 { fill: #e1e1e1 }
-  .terminal-3956614203-r2 { fill: #c5c8c6 }
-  .terminal-3956614203-r3 { fill: #004578 }
-  .terminal-3956614203-r4 { fill: #152939 }
-  .terminal-3956614203-r5 { fill: #1e1e1e }
-  .terminal-3956614203-r6 { fill: #e1e1e1;text-decoration: underline; }
-  .terminal-3956614203-r7 { fill: #dde8f3;font-weight: bold }
-  .terminal-3956614203-r8 { fill: #ddedf9 }
+      .terminal-1351164996-r1 { fill: #e1e1e1 }
+  .terminal-1351164996-r2 { fill: #c5c8c6 }
+  .terminal-1351164996-r3 { fill: #004578 }
+  .terminal-1351164996-r4 { fill: #152939 }
+  .terminal-1351164996-r5 { fill: #1e1e1e }
+  .terminal-1351164996-r6 { fill: #e1e1e1;text-decoration: underline; }
+  .terminal-1351164996-r7 { fill: #dde8f3;font-weight: bold }
+  .terminal-1351164996-r8 { fill: #ddedf9 }
       </style>
   
       <defs>
-      <clipPath id="terminal-3956614203-clip-terminal">
+      <clipPath id="terminal-1351164996-clip-terminal">
         <rect x="0" y="0" width="975.0" height="584.5999999999999" />
       </clipPath>
-      <clipPath id="terminal-3956614203-line-0">
+      <clipPath id="terminal-1351164996-line-0">
       <rect x="0" y="1.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-1">
+  <clipPath id="terminal-1351164996-line-1">
       <rect x="0" y="25.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-2">
+  <clipPath id="terminal-1351164996-line-2">
       <rect x="0" y="50.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-3">
+  <clipPath id="terminal-1351164996-line-3">
       <rect x="0" y="74.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-4">
+  <clipPath id="terminal-1351164996-line-4">
       <rect x="0" y="99.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-5">
+  <clipPath id="terminal-1351164996-line-5">
       <rect x="0" y="123.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-6">
+  <clipPath id="terminal-1351164996-line-6">
       <rect x="0" y="147.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-7">
+  <clipPath id="terminal-1351164996-line-7">
       <rect x="0" y="172.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-8">
+  <clipPath id="terminal-1351164996-line-8">
       <rect x="0" y="196.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-9">
+  <clipPath id="terminal-1351164996-line-9">
       <rect x="0" y="221.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-10">
+  <clipPath id="terminal-1351164996-line-10">
       <rect x="0" y="245.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-11">
+  <clipPath id="terminal-1351164996-line-11">
       <rect x="0" y="269.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-12">
+  <clipPath id="terminal-1351164996-line-12">
       <rect x="0" y="294.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-13">
+  <clipPath id="terminal-1351164996-line-13">
       <rect x="0" y="318.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-14">
+  <clipPath id="terminal-1351164996-line-14">
       <rect x="0" y="343.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-15">
+  <clipPath id="terminal-1351164996-line-15">
       <rect x="0" y="367.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-16">
+  <clipPath id="terminal-1351164996-line-16">
       <rect x="0" y="391.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-17">
+  <clipPath id="terminal-1351164996-line-17">
       <rect x="0" y="416.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-18">
+  <clipPath id="terminal-1351164996-line-18">
       <rect x="0" y="440.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-19">
+  <clipPath id="terminal-1351164996-line-19">
       <rect x="0" y="465.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-20">
+  <clipPath id="terminal-1351164996-line-20">
       <rect x="0" y="489.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-21">
+  <clipPath id="terminal-1351164996-line-21">
       <rect x="0" y="513.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3956614203-line-22">
+  <clipPath id="terminal-1351164996-line-22">
       <rect x="0" y="538.3" width="976" height="24.65"/>
               </clipPath>
       </defs>
   
-      <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/><text class="terminal-3956614203-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">StyledProgressBar</text>
+      <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/><text class="terminal-1351164996-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">StyledProgressBar</text>
               <g transform="translate(26,22)">
               <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
               <circle cx="22" cy="0" r="7" fill="#febc2e"/>
               <circle cx="44" cy="0" r="7" fill="#28c840"/>
               </g>
           
-      <g transform="translate(9, 41)" clip-path="url(#terminal-3956614203-clip-terminal)">
+      <g transform="translate(9, 41)" clip-path="url(#terminal-1351164996-clip-terminal)">
       <rect fill="#1e1e1e" x="0" y="1.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="1.5" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="1.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="25.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="25.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="50.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="50.3" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="50.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="74.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="74.7" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="74.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="99.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="99.1" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="99.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="123.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="123.5" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="123.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="147.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="147.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="172.3" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="172.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="196.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="196.7" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="196.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="221.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="221.1" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="221.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="245.5" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="245.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="269.9" width="146.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="366" y="269.9" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="597.8" y="269.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#fea62b" x="622.2" y="269.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="658.8" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="671" y="269.9" width="97.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="269.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="294.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="294.3" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="294.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="318.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="318.7" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="318.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="343.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="343.1" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="343.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="367.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="367.5" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="367.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="391.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="391.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="391.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="416.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="416.3" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="416.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="440.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="440.7" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="440.7" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="465.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="465.1" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="465.1" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="489.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="489.5" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="489.5" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="513.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="513.9" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="513.9" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="538.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="538.3" width="561.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="768.6" y="538.3" width="207.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#0053aa" x="0" y="562.7" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#0178d4" x="36.6" y="562.7" width="85.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#0178d4" x="122" y="562.7" width="854" height="24.65" shape-rendering="crispEdges"/>
-      <g class="terminal-3956614203-matrix">
-      <text class="terminal-3956614203-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-3956614203-line-0)">
-  </text><text class="terminal-3956614203-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-3956614203-line-1)">
-  </text><text class="terminal-3956614203-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-3956614203-line-2)">
-  </text><text class="terminal-3956614203-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-3956614203-line-3)">
-  </text><text class="terminal-3956614203-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-3956614203-line-4)">
-  </text><text class="terminal-3956614203-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-3956614203-line-5)">
-  </text><text class="terminal-3956614203-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-3956614203-line-6)">
-  </text><text class="terminal-3956614203-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-3956614203-line-7)">
-  </text><text class="terminal-3956614203-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-3956614203-line-8)">
-  </text><text class="terminal-3956614203-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-3956614203-line-9)">
-  </text><text class="terminal-3956614203-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-3956614203-line-10)">
-  </text><text class="terminal-3956614203-r3" x="207.4" y="288.4" textLength="146.4" clip-path="url(#terminal-3956614203-line-11)">━━━━━━━━━━━━</text><text class="terminal-3956614203-r3" x="353.8" y="288.4" textLength="12.2" clip-path="url(#terminal-3956614203-line-11)">╸</text><text class="terminal-3956614203-r4" x="366" y="288.4" textLength="231.8" clip-path="url(#terminal-3956614203-line-11)">━━━━━━━━━━━━━━━━━━━</text><text class="terminal-3956614203-r5" x="622.2" y="288.4" textLength="36.6" clip-path="url(#terminal-3956614203-line-11)">39%</text><text class="terminal-3956614203-r6" x="671" y="288.4" textLength="97.6" clip-path="url(#terminal-3956614203-line-11)">00:00:07</text><text class="terminal-3956614203-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-3956614203-line-11)">
-  </text><text class="terminal-3956614203-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-3956614203-line-12)">
-  </text><text class="terminal-3956614203-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-3956614203-line-13)">
-  </text><text class="terminal-3956614203-r2" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-3956614203-line-14)">
-  </text><text class="terminal-3956614203-r2" x="976" y="386" textLength="12.2" clip-path="url(#terminal-3956614203-line-15)">
-  </text><text class="terminal-3956614203-r2" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-3956614203-line-16)">
-  </text><text class="terminal-3956614203-r2" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-3956614203-line-17)">
-  </text><text class="terminal-3956614203-r2" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-3956614203-line-18)">
-  </text><text class="terminal-3956614203-r2" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-3956614203-line-19)">
-  </text><text class="terminal-3956614203-r2" x="976" y="508" textLength="12.2" clip-path="url(#terminal-3956614203-line-20)">
-  </text><text class="terminal-3956614203-r2" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-3956614203-line-21)">
-  </text><text class="terminal-3956614203-r2" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-3956614203-line-22)">
-  </text><text class="terminal-3956614203-r7" x="0" y="581.2" textLength="36.6" clip-path="url(#terminal-3956614203-line-23)">&#160;S&#160;</text><text class="terminal-3956614203-r8" x="36.6" y="581.2" textLength="85.4" clip-path="url(#terminal-3956614203-line-23)">&#160;Start&#160;</text>
+      <g class="terminal-1351164996-matrix">
+      <text class="terminal-1351164996-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1351164996-line-0)">
+  </text><text class="terminal-1351164996-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1351164996-line-1)">
+  </text><text class="terminal-1351164996-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1351164996-line-2)">
+  </text><text class="terminal-1351164996-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1351164996-line-3)">
+  </text><text class="terminal-1351164996-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1351164996-line-4)">
+  </text><text class="terminal-1351164996-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1351164996-line-5)">
+  </text><text class="terminal-1351164996-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1351164996-line-6)">
+  </text><text class="terminal-1351164996-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1351164996-line-7)">
+  </text><text class="terminal-1351164996-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1351164996-line-8)">
+  </text><text class="terminal-1351164996-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1351164996-line-9)">
+  </text><text class="terminal-1351164996-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1351164996-line-10)">
+  </text><text class="terminal-1351164996-r3" x="207.4" y="288.4" textLength="146.4" clip-path="url(#terminal-1351164996-line-11)">━━━━━━━━━━━━</text><text class="terminal-1351164996-r4" x="353.8" y="288.4" textLength="12.2" clip-path="url(#terminal-1351164996-line-11)">╺</text><text class="terminal-1351164996-r4" x="366" y="288.4" textLength="231.8" clip-path="url(#terminal-1351164996-line-11)">━━━━━━━━━━━━━━━━━━━</text><text class="terminal-1351164996-r5" x="622.2" y="288.4" textLength="36.6" clip-path="url(#terminal-1351164996-line-11)">39%</text><text class="terminal-1351164996-r6" x="671" y="288.4" textLength="97.6" clip-path="url(#terminal-1351164996-line-11)">00:00:07</text><text class="terminal-1351164996-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1351164996-line-11)">
+  </text><text class="terminal-1351164996-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1351164996-line-12)">
+  </text><text class="terminal-1351164996-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1351164996-line-13)">
+  </text><text class="terminal-1351164996-r2" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1351164996-line-14)">
+  </text><text class="terminal-1351164996-r2" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1351164996-line-15)">
+  </text><text class="terminal-1351164996-r2" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1351164996-line-16)">
+  </text><text class="terminal-1351164996-r2" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1351164996-line-17)">
+  </text><text class="terminal-1351164996-r2" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1351164996-line-18)">
+  </text><text class="terminal-1351164996-r2" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1351164996-line-19)">
+  </text><text class="terminal-1351164996-r2" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1351164996-line-20)">
+  </text><text class="terminal-1351164996-r2" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1351164996-line-21)">
+  </text><text class="terminal-1351164996-r2" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1351164996-line-22)">
+  </text><text class="terminal-1351164996-r7" x="0" y="581.2" textLength="36.6" clip-path="url(#terminal-1351164996-line-23)">&#160;S&#160;</text><text class="terminal-1351164996-r8" x="36.6" y="581.2" textLength="85.4" clip-path="url(#terminal-1351164996-line-23)">&#160;Start&#160;</text>
       </g>
       </g>
   </svg>
@@ -29774,135 +29774,135 @@
           font-weight: 700;
       }
   
-      .terminal-904522218-matrix {
+      .terminal-1786282230-matrix {
           font-family: Fira Code, monospace;
           font-size: 20px;
           line-height: 24.4px;
           font-variant-east-asian: full-width;
       }
   
-      .terminal-904522218-title {
+      .terminal-1786282230-title {
           font-size: 18px;
           font-weight: bold;
           font-family: arial;
       }
   
-      .terminal-904522218-r1 { fill: #ff0000 }
-  .terminal-904522218-r2 { fill: #c5c8c6 }
-  .terminal-904522218-r3 { fill: #e1e1e1;font-weight: bold }
-  .terminal-904522218-r4 { fill: #e1e1e1 }
-  .terminal-904522218-r5 { fill: #fea62b }
-  .terminal-904522218-r6 { fill: #323232 }
+      .terminal-1786282230-r1 { fill: #ff0000 }
+  .terminal-1786282230-r2 { fill: #c5c8c6 }
+  .terminal-1786282230-r3 { fill: #e1e1e1;font-weight: bold }
+  .terminal-1786282230-r4 { fill: #e1e1e1 }
+  .terminal-1786282230-r5 { fill: #fea62b }
+  .terminal-1786282230-r6 { fill: #323232 }
       </style>
   
       <defs>
-      <clipPath id="terminal-904522218-clip-terminal">
+      <clipPath id="terminal-1786282230-clip-terminal">
         <rect x="0" y="0" width="975.0" height="584.5999999999999" />
       </clipPath>
-      <clipPath id="terminal-904522218-line-0">
+      <clipPath id="terminal-1786282230-line-0">
       <rect x="0" y="1.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-1">
+  <clipPath id="terminal-1786282230-line-1">
       <rect x="0" y="25.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-2">
+  <clipPath id="terminal-1786282230-line-2">
       <rect x="0" y="50.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-3">
+  <clipPath id="terminal-1786282230-line-3">
       <rect x="0" y="74.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-4">
+  <clipPath id="terminal-1786282230-line-4">
       <rect x="0" y="99.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-5">
+  <clipPath id="terminal-1786282230-line-5">
       <rect x="0" y="123.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-6">
+  <clipPath id="terminal-1786282230-line-6">
       <rect x="0" y="147.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-7">
+  <clipPath id="terminal-1786282230-line-7">
       <rect x="0" y="172.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-8">
+  <clipPath id="terminal-1786282230-line-8">
       <rect x="0" y="196.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-9">
+  <clipPath id="terminal-1786282230-line-9">
       <rect x="0" y="221.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-10">
+  <clipPath id="terminal-1786282230-line-10">
       <rect x="0" y="245.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-11">
+  <clipPath id="terminal-1786282230-line-11">
       <rect x="0" y="269.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-12">
+  <clipPath id="terminal-1786282230-line-12">
       <rect x="0" y="294.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-13">
+  <clipPath id="terminal-1786282230-line-13">
       <rect x="0" y="318.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-14">
+  <clipPath id="terminal-1786282230-line-14">
       <rect x="0" y="343.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-15">
+  <clipPath id="terminal-1786282230-line-15">
       <rect x="0" y="367.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-16">
+  <clipPath id="terminal-1786282230-line-16">
       <rect x="0" y="391.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-17">
+  <clipPath id="terminal-1786282230-line-17">
       <rect x="0" y="416.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-18">
+  <clipPath id="terminal-1786282230-line-18">
       <rect x="0" y="440.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-19">
+  <clipPath id="terminal-1786282230-line-19">
       <rect x="0" y="465.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-20">
+  <clipPath id="terminal-1786282230-line-20">
       <rect x="0" y="489.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-21">
+  <clipPath id="terminal-1786282230-line-21">
       <rect x="0" y="513.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-904522218-line-22">
+  <clipPath id="terminal-1786282230-line-22">
       <rect x="0" y="538.3" width="976" height="24.65"/>
               </clipPath>
       </defs>
   
-      <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/><text class="terminal-904522218-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">RecomposeApp</text>
+      <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/><text class="terminal-1786282230-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">RecomposeApp</text>
               <g transform="translate(26,22)">
               <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
               <circle cx="22" cy="0" r="7" fill="#febc2e"/>
               <circle cx="44" cy="0" r="7" fill="#28c840"/>
               </g>
           
-      <g transform="translate(9, 41)" clip-path="url(#terminal-904522218-clip-terminal)">
-      <rect fill="#1e1e1e" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="12.2" y="1.5" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="122" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="134.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="146.4" y="1.5" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="268.4" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="280.6" y="1.5" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="402.6" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="427" y="1.5" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="536.8" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="549" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="1.5" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="683.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="695.4" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="707.6" y="1.5" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="817.4" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="829.6" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="841.8" y="1.5" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="12.2" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="85.4" y="25.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="122" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="134.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="146.4" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="219.6" y="25.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="268.4" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="280.6" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="25.9" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="402.6" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="427" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="500.2" y="25.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="536.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="549" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="634.4" y="25.9" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="683.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="695.4" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="707.6" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="780.8" y="25.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="817.4" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="829.6" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="841.8" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="915" y="25.9" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="12.2" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="85.4" y="50.3" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="122" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="134.2" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="146.4" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="219.6" y="50.3" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="268.4" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="280.6" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="50.3" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="402.6" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="427" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="500.2" y="50.3" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="536.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="549" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="634.4" y="50.3" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="683.2" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="695.4" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="707.6" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="780.8" y="50.3" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="817.4" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="829.6" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="841.8" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="915" y="50.3" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="12.2" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="85.4" y="74.7" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="122" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="134.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="146.4" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="219.6" y="74.7" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="268.4" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="280.6" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="74.7" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="402.6" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="427" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="500.2" y="74.7" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="536.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="549" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="634.4" y="74.7" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="683.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="695.4" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="707.6" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="780.8" y="74.7" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="817.4" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="829.6" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="841.8" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="915" y="74.7" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="12.2" y="99.1" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="122" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="134.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="146.4" y="99.1" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="268.4" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="280.6" y="99.1" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="402.6" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="427" y="99.1" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="536.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="549" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="99.1" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="683.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="695.4" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="707.6" y="99.1" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="817.4" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="829.6" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="841.8" y="99.1" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="294.3" width="195.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="195.2" y="294.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="294.3" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="390.4" y="294.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="294.3" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="451.4" y="294.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="463.6" y="294.3" width="97.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="294.3" width="414.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="489.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="513.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="538.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="562.7" width="976" height="24.65" shape-rendering="crispEdges"/>
-      <g class="terminal-904522218-matrix">
-      <text class="terminal-904522218-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┌</text><text class="terminal-904522218-r1" x="12.2" y="20" textLength="109.8" clip-path="url(#terminal-904522218-line-0)">─────────</text><text class="terminal-904522218-r1" x="122" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┐</text><text class="terminal-904522218-r1" x="134.2" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┌</text><text class="terminal-904522218-r1" x="146.4" y="20" textLength="109.8" clip-path="url(#terminal-904522218-line-0)">─────────</text><text class="terminal-904522218-r1" x="256.2" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┐</text><text class="terminal-904522218-r1" x="268.4" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┌</text><text class="terminal-904522218-r1" x="280.6" y="20" textLength="122" clip-path="url(#terminal-904522218-line-0)">──────────</text><text class="terminal-904522218-r1" x="402.6" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┐</text><text class="terminal-904522218-r1" x="414.8" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┌</text><text class="terminal-904522218-r1" x="427" y="20" textLength="109.8" clip-path="url(#terminal-904522218-line-0)">─────────</text><text class="terminal-904522218-r1" x="536.8" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┐</text><text class="terminal-904522218-r1" x="549" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┌</text><text class="terminal-904522218-r1" x="561.2" y="20" textLength="122" clip-path="url(#terminal-904522218-line-0)">──────────</text><text class="terminal-904522218-r1" x="683.2" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┐</text><text class="terminal-904522218-r1" x="695.4" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┌</text><text class="terminal-904522218-r1" x="707.6" y="20" textLength="109.8" clip-path="url(#terminal-904522218-line-0)">─────────</text><text class="terminal-904522218-r1" x="817.4" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┐</text><text class="terminal-904522218-r1" x="829.6" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┌</text><text class="terminal-904522218-r1" x="841.8" y="20" textLength="122" clip-path="url(#terminal-904522218-line-0)">──────────</text><text class="terminal-904522218-r1" x="963.8" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">┐</text><text class="terminal-904522218-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-904522218-line-0)">
-  </text><text class="terminal-904522218-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r3" x="12.2" y="44.4" textLength="73.2" clip-path="url(#terminal-904522218-line-1)">&#160;┓&#160;┏━┓</text><text class="terminal-904522218-r1" x="122" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r1" x="134.2" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r3" x="146.4" y="44.4" textLength="73.2" clip-path="url(#terminal-904522218-line-1)">&#160;┓&#160;&#160;┓&#160;</text><text class="terminal-904522218-r1" x="256.2" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r1" x="268.4" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r3" x="280.6" y="44.4" textLength="73.2" clip-path="url(#terminal-904522218-line-1)">&#160;┓&#160;╺━┓</text><text class="terminal-904522218-r1" x="402.6" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r1" x="414.8" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r3" x="427" y="44.4" textLength="73.2" clip-path="url(#terminal-904522218-line-1)">&#160;┓&#160;╺━┓</text><text class="terminal-904522218-r1" x="536.8" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r1" x="549" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r3" x="561.2" y="44.4" textLength="73.2" clip-path="url(#terminal-904522218-line-1)">&#160;┓&#160;╻&#160;╻</text><text class="terminal-904522218-r1" x="683.2" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r1" x="695.4" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r3" x="707.6" y="44.4" textLength="73.2" clip-path="url(#terminal-904522218-line-1)">&#160;┓&#160;┏━╸</text><text class="terminal-904522218-r1" x="817.4" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r1" x="829.6" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r3" x="841.8" y="44.4" textLength="73.2" clip-path="url(#terminal-904522218-line-1)">&#160;┓&#160;┏━╸</text><text class="terminal-904522218-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">│</text><text class="terminal-904522218-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-904522218-line-1)">
-  </text><text class="terminal-904522218-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r3" x="12.2" y="68.8" textLength="73.2" clip-path="url(#terminal-904522218-line-2)">&#160;┃&#160;┃&#160;┃</text><text class="terminal-904522218-r1" x="122" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r1" x="134.2" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r3" x="146.4" y="68.8" textLength="73.2" clip-path="url(#terminal-904522218-line-2)">&#160;┃&#160;&#160;┃&#160;</text><text class="terminal-904522218-r1" x="256.2" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r1" x="268.4" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r3" x="280.6" y="68.8" textLength="73.2" clip-path="url(#terminal-904522218-line-2)">&#160;┃&#160;┏━┛</text><text class="terminal-904522218-r1" x="402.6" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r1" x="414.8" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r3" x="427" y="68.8" textLength="73.2" clip-path="url(#terminal-904522218-line-2)">&#160;┃&#160;&#160;━┫</text><text class="terminal-904522218-r1" x="536.8" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r1" x="549" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r3" x="561.2" y="68.8" textLength="73.2" clip-path="url(#terminal-904522218-line-2)">&#160;┃&#160;┗━┫</text><text class="terminal-904522218-r1" x="683.2" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r1" x="695.4" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r3" x="707.6" y="68.8" textLength="73.2" clip-path="url(#terminal-904522218-line-2)">&#160;┃&#160;┗━┓</text><text class="terminal-904522218-r1" x="817.4" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r1" x="829.6" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r3" x="841.8" y="68.8" textLength="73.2" clip-path="url(#terminal-904522218-line-2)">&#160;┃&#160;┣━┓</text><text class="terminal-904522218-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">│</text><text class="terminal-904522218-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-904522218-line-2)">
-  </text><text class="terminal-904522218-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r3" x="12.2" y="93.2" textLength="73.2" clip-path="url(#terminal-904522218-line-3)">╺┻╸┗━┛</text><text class="terminal-904522218-r1" x="122" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r1" x="134.2" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r3" x="146.4" y="93.2" textLength="73.2" clip-path="url(#terminal-904522218-line-3)">╺┻╸╺┻╸</text><text class="terminal-904522218-r1" x="256.2" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r1" x="268.4" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r3" x="280.6" y="93.2" textLength="73.2" clip-path="url(#terminal-904522218-line-3)">╺┻╸┗━╸</text><text class="terminal-904522218-r1" x="402.6" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r1" x="414.8" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r3" x="427" y="93.2" textLength="73.2" clip-path="url(#terminal-904522218-line-3)">╺┻╸╺━┛</text><text class="terminal-904522218-r1" x="536.8" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r1" x="549" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r3" x="561.2" y="93.2" textLength="73.2" clip-path="url(#terminal-904522218-line-3)">╺┻╸&#160;&#160;╹</text><text class="terminal-904522218-r1" x="683.2" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r1" x="695.4" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r3" x="707.6" y="93.2" textLength="73.2" clip-path="url(#terminal-904522218-line-3)">╺┻╸╺━┛</text><text class="terminal-904522218-r1" x="817.4" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r1" x="829.6" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r3" x="841.8" y="93.2" textLength="73.2" clip-path="url(#terminal-904522218-line-3)">╺┻╸┗━┛</text><text class="terminal-904522218-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">│</text><text class="terminal-904522218-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-904522218-line-3)">
-  </text><text class="terminal-904522218-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">└</text><text class="terminal-904522218-r1" x="12.2" y="117.6" textLength="109.8" clip-path="url(#terminal-904522218-line-4)">─────────</text><text class="terminal-904522218-r1" x="122" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">┘</text><text class="terminal-904522218-r1" x="134.2" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">└</text><text class="terminal-904522218-r1" x="146.4" y="117.6" textLength="109.8" clip-path="url(#terminal-904522218-line-4)">─────────</text><text class="terminal-904522218-r1" x="256.2" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">┘</text><text class="terminal-904522218-r1" x="268.4" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">└</text><text class="terminal-904522218-r1" x="280.6" y="117.6" textLength="122" clip-path="url(#terminal-904522218-line-4)">──────────</text><text class="terminal-904522218-r1" x="402.6" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">┘</text><text class="terminal-904522218-r1" x="414.8" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">└</text><text class="terminal-904522218-r1" x="427" y="117.6" textLength="109.8" clip-path="url(#terminal-904522218-line-4)">─────────</text><text class="terminal-904522218-r1" x="536.8" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">┘</text><text class="terminal-904522218-r1" x="549" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">└</text><text class="terminal-904522218-r1" x="561.2" y="117.6" textLength="122" clip-path="url(#terminal-904522218-line-4)">──────────</text><text class="terminal-904522218-r1" x="683.2" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">┘</text><text class="terminal-904522218-r1" x="695.4" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">└</text><text class="terminal-904522218-r1" x="707.6" y="117.6" textLength="109.8" clip-path="url(#terminal-904522218-line-4)">─────────</text><text class="terminal-904522218-r1" x="817.4" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">┘</text><text class="terminal-904522218-r1" x="829.6" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">└</text><text class="terminal-904522218-r1" x="841.8" y="117.6" textLength="122" clip-path="url(#terminal-904522218-line-4)">──────────</text><text class="terminal-904522218-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">┘</text><text class="terminal-904522218-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-904522218-line-4)">
-  </text><text class="terminal-904522218-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-904522218-line-5)">
-  </text><text class="terminal-904522218-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-904522218-line-6)">
-  </text><text class="terminal-904522218-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-904522218-line-7)">
-  </text><text class="terminal-904522218-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-904522218-line-8)">
-  </text><text class="terminal-904522218-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-904522218-line-9)">
-  </text><text class="terminal-904522218-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-904522218-line-10)">
-  </text><text class="terminal-904522218-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-904522218-line-11)">
-  </text><text class="terminal-904522218-r5" x="0" y="312.8" textLength="195.2" clip-path="url(#terminal-904522218-line-12)">━━━━━━━━━━━━━━━━</text><text class="terminal-904522218-r6" x="195.2" y="312.8" textLength="12.2" clip-path="url(#terminal-904522218-line-12)">╺</text><text class="terminal-904522218-r6" x="207.4" y="312.8" textLength="183" clip-path="url(#terminal-904522218-line-12)">━━━━━━━━━━━━━━━</text><text class="terminal-904522218-r4" x="414.8" y="312.8" textLength="36.6" clip-path="url(#terminal-904522218-line-12)">50%</text><text class="terminal-904522218-r4" x="463.6" y="312.8" textLength="97.6" clip-path="url(#terminal-904522218-line-12)">--:--:--</text><text class="terminal-904522218-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-904522218-line-12)">
-  </text><text class="terminal-904522218-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-904522218-line-13)">
-  </text><text class="terminal-904522218-r2" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-904522218-line-14)">
-  </text><text class="terminal-904522218-r2" x="976" y="386" textLength="12.2" clip-path="url(#terminal-904522218-line-15)">
-  </text><text class="terminal-904522218-r2" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-904522218-line-16)">
-  </text><text class="terminal-904522218-r2" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-904522218-line-17)">
-  </text><text class="terminal-904522218-r2" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-904522218-line-18)">
-  </text><text class="terminal-904522218-r2" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-904522218-line-19)">
-  </text><text class="terminal-904522218-r2" x="976" y="508" textLength="12.2" clip-path="url(#terminal-904522218-line-20)">
-  </text><text class="terminal-904522218-r2" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-904522218-line-21)">
-  </text><text class="terminal-904522218-r2" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-904522218-line-22)">
+      <g transform="translate(9, 41)" clip-path="url(#terminal-1786282230-clip-terminal)">
+      <rect fill="#1e1e1e" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="12.2" y="1.5" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="122" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="134.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="146.4" y="1.5" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="268.4" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="280.6" y="1.5" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="402.6" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="427" y="1.5" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="536.8" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="549" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="1.5" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="683.2" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="695.4" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="707.6" y="1.5" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="817.4" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="829.6" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="841.8" y="1.5" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="12.2" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="85.4" y="25.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="122" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="134.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="146.4" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="219.6" y="25.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="268.4" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="280.6" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="25.9" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="402.6" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="427" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="500.2" y="25.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="536.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="549" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="634.4" y="25.9" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="683.2" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="695.4" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="707.6" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="780.8" y="25.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="817.4" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="829.6" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="841.8" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="915" y="25.9" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="12.2" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="85.4" y="50.3" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="122" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="134.2" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="146.4" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="219.6" y="50.3" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="268.4" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="280.6" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="50.3" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="402.6" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="427" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="500.2" y="50.3" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="536.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="549" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="634.4" y="50.3" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="683.2" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="695.4" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="707.6" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="780.8" y="50.3" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="817.4" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="829.6" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="841.8" y="50.3" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="915" y="50.3" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="12.2" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="85.4" y="74.7" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="122" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="134.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="146.4" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="219.6" y="74.7" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="268.4" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="280.6" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="353.8" y="74.7" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="402.6" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="427" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="500.2" y="74.7" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="536.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="549" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="634.4" y="74.7" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="683.2" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="695.4" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="707.6" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="780.8" y="74.7" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="817.4" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="829.6" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="841.8" y="74.7" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="915" y="74.7" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="12.2" y="99.1" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="122" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="134.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="146.4" y="99.1" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="268.4" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="280.6" y="99.1" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="402.6" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="427" y="99.1" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="536.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="549" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="99.1" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="683.2" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="695.4" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="707.6" y="99.1" width="109.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="817.4" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="829.6" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="841.8" y="99.1" width="122" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="294.3" width="195.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="195.2" y="294.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="207.4" y="294.3" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="390.4" y="294.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="294.3" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="451.4" y="294.3" width="524.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="489.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="513.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="538.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="562.7" width="976" height="24.65" shape-rendering="crispEdges"/>
+      <g class="terminal-1786282230-matrix">
+      <text class="terminal-1786282230-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┌</text><text class="terminal-1786282230-r1" x="12.2" y="20" textLength="109.8" clip-path="url(#terminal-1786282230-line-0)">─────────</text><text class="terminal-1786282230-r1" x="122" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┐</text><text class="terminal-1786282230-r1" x="134.2" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┌</text><text class="terminal-1786282230-r1" x="146.4" y="20" textLength="109.8" clip-path="url(#terminal-1786282230-line-0)">─────────</text><text class="terminal-1786282230-r1" x="256.2" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┐</text><text class="terminal-1786282230-r1" x="268.4" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┌</text><text class="terminal-1786282230-r1" x="280.6" y="20" textLength="122" clip-path="url(#terminal-1786282230-line-0)">──────────</text><text class="terminal-1786282230-r1" x="402.6" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┐</text><text class="terminal-1786282230-r1" x="414.8" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┌</text><text class="terminal-1786282230-r1" x="427" y="20" textLength="109.8" clip-path="url(#terminal-1786282230-line-0)">─────────</text><text class="terminal-1786282230-r1" x="536.8" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┐</text><text class="terminal-1786282230-r1" x="549" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┌</text><text class="terminal-1786282230-r1" x="561.2" y="20" textLength="122" clip-path="url(#terminal-1786282230-line-0)">──────────</text><text class="terminal-1786282230-r1" x="683.2" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┐</text><text class="terminal-1786282230-r1" x="695.4" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┌</text><text class="terminal-1786282230-r1" x="707.6" y="20" textLength="109.8" clip-path="url(#terminal-1786282230-line-0)">─────────</text><text class="terminal-1786282230-r1" x="817.4" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┐</text><text class="terminal-1786282230-r1" x="829.6" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┌</text><text class="terminal-1786282230-r1" x="841.8" y="20" textLength="122" clip-path="url(#terminal-1786282230-line-0)">──────────</text><text class="terminal-1786282230-r1" x="963.8" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">┐</text><text class="terminal-1786282230-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1786282230-line-0)">
+  </text><text class="terminal-1786282230-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r3" x="12.2" y="44.4" textLength="73.2" clip-path="url(#terminal-1786282230-line-1)">&#160;┓&#160;┏━┓</text><text class="terminal-1786282230-r1" x="122" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r1" x="134.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r3" x="146.4" y="44.4" textLength="73.2" clip-path="url(#terminal-1786282230-line-1)">&#160;┓&#160;&#160;┓&#160;</text><text class="terminal-1786282230-r1" x="256.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r1" x="268.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r3" x="280.6" y="44.4" textLength="73.2" clip-path="url(#terminal-1786282230-line-1)">&#160;┓&#160;╺━┓</text><text class="terminal-1786282230-r1" x="402.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r1" x="414.8" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r3" x="427" y="44.4" textLength="73.2" clip-path="url(#terminal-1786282230-line-1)">&#160;┓&#160;╺━┓</text><text class="terminal-1786282230-r1" x="536.8" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r1" x="549" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r3" x="561.2" y="44.4" textLength="73.2" clip-path="url(#terminal-1786282230-line-1)">&#160;┓&#160;╻&#160;╻</text><text class="terminal-1786282230-r1" x="683.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r1" x="695.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r3" x="707.6" y="44.4" textLength="73.2" clip-path="url(#terminal-1786282230-line-1)">&#160;┓&#160;┏━╸</text><text class="terminal-1786282230-r1" x="817.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r1" x="829.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r3" x="841.8" y="44.4" textLength="73.2" clip-path="url(#terminal-1786282230-line-1)">&#160;┓&#160;┏━╸</text><text class="terminal-1786282230-r1" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">│</text><text class="terminal-1786282230-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-1)">
+  </text><text class="terminal-1786282230-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r3" x="12.2" y="68.8" textLength="73.2" clip-path="url(#terminal-1786282230-line-2)">&#160;┃&#160;┃&#160;┃</text><text class="terminal-1786282230-r1" x="122" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r1" x="134.2" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r3" x="146.4" y="68.8" textLength="73.2" clip-path="url(#terminal-1786282230-line-2)">&#160;┃&#160;&#160;┃&#160;</text><text class="terminal-1786282230-r1" x="256.2" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r1" x="268.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r3" x="280.6" y="68.8" textLength="73.2" clip-path="url(#terminal-1786282230-line-2)">&#160;┃&#160;┏━┛</text><text class="terminal-1786282230-r1" x="402.6" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r1" x="414.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r3" x="427" y="68.8" textLength="73.2" clip-path="url(#terminal-1786282230-line-2)">&#160;┃&#160;&#160;━┫</text><text class="terminal-1786282230-r1" x="536.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r1" x="549" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r3" x="561.2" y="68.8" textLength="73.2" clip-path="url(#terminal-1786282230-line-2)">&#160;┃&#160;┗━┫</text><text class="terminal-1786282230-r1" x="683.2" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r1" x="695.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r3" x="707.6" y="68.8" textLength="73.2" clip-path="url(#terminal-1786282230-line-2)">&#160;┃&#160;┗━┓</text><text class="terminal-1786282230-r1" x="817.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r1" x="829.6" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r3" x="841.8" y="68.8" textLength="73.2" clip-path="url(#terminal-1786282230-line-2)">&#160;┃&#160;┣━┓</text><text class="terminal-1786282230-r1" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">│</text><text class="terminal-1786282230-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-2)">
+  </text><text class="terminal-1786282230-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r3" x="12.2" y="93.2" textLength="73.2" clip-path="url(#terminal-1786282230-line-3)">╺┻╸┗━┛</text><text class="terminal-1786282230-r1" x="122" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r1" x="134.2" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r3" x="146.4" y="93.2" textLength="73.2" clip-path="url(#terminal-1786282230-line-3)">╺┻╸╺┻╸</text><text class="terminal-1786282230-r1" x="256.2" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r1" x="268.4" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r3" x="280.6" y="93.2" textLength="73.2" clip-path="url(#terminal-1786282230-line-3)">╺┻╸┗━╸</text><text class="terminal-1786282230-r1" x="402.6" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r1" x="414.8" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r3" x="427" y="93.2" textLength="73.2" clip-path="url(#terminal-1786282230-line-3)">╺┻╸╺━┛</text><text class="terminal-1786282230-r1" x="536.8" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r1" x="549" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r3" x="561.2" y="93.2" textLength="73.2" clip-path="url(#terminal-1786282230-line-3)">╺┻╸&#160;&#160;╹</text><text class="terminal-1786282230-r1" x="683.2" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r1" x="695.4" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r3" x="707.6" y="93.2" textLength="73.2" clip-path="url(#terminal-1786282230-line-3)">╺┻╸╺━┛</text><text class="terminal-1786282230-r1" x="817.4" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r1" x="829.6" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r3" x="841.8" y="93.2" textLength="73.2" clip-path="url(#terminal-1786282230-line-3)">╺┻╸┗━┛</text><text class="terminal-1786282230-r1" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">│</text><text class="terminal-1786282230-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-3)">
+  </text><text class="terminal-1786282230-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">└</text><text class="terminal-1786282230-r1" x="12.2" y="117.6" textLength="109.8" clip-path="url(#terminal-1786282230-line-4)">─────────</text><text class="terminal-1786282230-r1" x="122" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">┘</text><text class="terminal-1786282230-r1" x="134.2" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">└</text><text class="terminal-1786282230-r1" x="146.4" y="117.6" textLength="109.8" clip-path="url(#terminal-1786282230-line-4)">─────────</text><text class="terminal-1786282230-r1" x="256.2" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">┘</text><text class="terminal-1786282230-r1" x="268.4" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">└</text><text class="terminal-1786282230-r1" x="280.6" y="117.6" textLength="122" clip-path="url(#terminal-1786282230-line-4)">──────────</text><text class="terminal-1786282230-r1" x="402.6" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">┘</text><text class="terminal-1786282230-r1" x="414.8" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">└</text><text class="terminal-1786282230-r1" x="427" y="117.6" textLength="109.8" clip-path="url(#terminal-1786282230-line-4)">─────────</text><text class="terminal-1786282230-r1" x="536.8" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">┘</text><text class="terminal-1786282230-r1" x="549" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">└</text><text class="terminal-1786282230-r1" x="561.2" y="117.6" textLength="122" clip-path="url(#terminal-1786282230-line-4)">──────────</text><text class="terminal-1786282230-r1" x="683.2" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">┘</text><text class="terminal-1786282230-r1" x="695.4" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">└</text><text class="terminal-1786282230-r1" x="707.6" y="117.6" textLength="109.8" clip-path="url(#terminal-1786282230-line-4)">─────────</text><text class="terminal-1786282230-r1" x="817.4" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">┘</text><text class="terminal-1786282230-r1" x="829.6" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">└</text><text class="terminal-1786282230-r1" x="841.8" y="117.6" textLength="122" clip-path="url(#terminal-1786282230-line-4)">──────────</text><text class="terminal-1786282230-r1" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">┘</text><text class="terminal-1786282230-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-4)">
+  </text><text class="terminal-1786282230-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1786282230-line-5)">
+  </text><text class="terminal-1786282230-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-6)">
+  </text><text class="terminal-1786282230-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-7)">
+  </text><text class="terminal-1786282230-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-8)">
+  </text><text class="terminal-1786282230-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-9)">
+  </text><text class="terminal-1786282230-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1786282230-line-10)">
+  </text><text class="terminal-1786282230-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-11)">
+  </text><text class="terminal-1786282230-r5" x="0" y="312.8" textLength="195.2" clip-path="url(#terminal-1786282230-line-12)">━━━━━━━━━━━━━━━━</text><text class="terminal-1786282230-r6" x="195.2" y="312.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-12)">╺</text><text class="terminal-1786282230-r6" x="207.4" y="312.8" textLength="183" clip-path="url(#terminal-1786282230-line-12)">━━━━━━━━━━━━━━━</text><text class="terminal-1786282230-r4" x="414.8" y="312.8" textLength="36.6" clip-path="url(#terminal-1786282230-line-12)">50%</text><text class="terminal-1786282230-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-12)">
+  </text><text class="terminal-1786282230-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-13)">
+  </text><text class="terminal-1786282230-r2" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-14)">
+  </text><text class="terminal-1786282230-r2" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1786282230-line-15)">
+  </text><text class="terminal-1786282230-r2" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-16)">
+  </text><text class="terminal-1786282230-r2" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-17)">
+  </text><text class="terminal-1786282230-r2" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1786282230-line-18)">
+  </text><text class="terminal-1786282230-r2" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1786282230-line-19)">
+  </text><text class="terminal-1786282230-r2" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1786282230-line-20)">
+  </text><text class="terminal-1786282230-r2" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1786282230-line-21)">
+  </text><text class="terminal-1786282230-r2" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1786282230-line-22)">
   </text>
       </g>
       </g>
@@ -40274,134 +40274,134 @@
           font-weight: 700;
       }
   
-      .terminal-3455460968-matrix {
+      .terminal-3216424293-matrix {
           font-family: Fira Code, monospace;
           font-size: 20px;
           line-height: 24.4px;
           font-variant-east-asian: full-width;
       }
   
-      .terminal-3455460968-title {
+      .terminal-3216424293-title {
           font-size: 18px;
           font-weight: bold;
           font-family: arial;
       }
   
-      .terminal-3455460968-r1 { fill: #fea62b }
-  .terminal-3455460968-r2 { fill: #323232 }
-  .terminal-3455460968-r3 { fill: #c5c8c6 }
-  .terminal-3455460968-r4 { fill: #e1e1e1 }
-  .terminal-3455460968-r5 { fill: #e2e3e3 }
+      .terminal-3216424293-r1 { fill: #fea62b }
+  .terminal-3216424293-r2 { fill: #323232 }
+  .terminal-3216424293-r3 { fill: #c5c8c6 }
+  .terminal-3216424293-r4 { fill: #e1e1e1 }
+  .terminal-3216424293-r5 { fill: #e2e3e3 }
       </style>
   
       <defs>
-      <clipPath id="terminal-3455460968-clip-terminal">
+      <clipPath id="terminal-3216424293-clip-terminal">
         <rect x="0" y="0" width="975.0" height="584.5999999999999" />
       </clipPath>
-      <clipPath id="terminal-3455460968-line-0">
+      <clipPath id="terminal-3216424293-line-0">
       <rect x="0" y="1.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-1">
+  <clipPath id="terminal-3216424293-line-1">
       <rect x="0" y="25.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-2">
+  <clipPath id="terminal-3216424293-line-2">
       <rect x="0" y="50.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-3">
+  <clipPath id="terminal-3216424293-line-3">
       <rect x="0" y="74.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-4">
+  <clipPath id="terminal-3216424293-line-4">
       <rect x="0" y="99.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-5">
+  <clipPath id="terminal-3216424293-line-5">
       <rect x="0" y="123.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-6">
+  <clipPath id="terminal-3216424293-line-6">
       <rect x="0" y="147.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-7">
+  <clipPath id="terminal-3216424293-line-7">
       <rect x="0" y="172.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-8">
+  <clipPath id="terminal-3216424293-line-8">
       <rect x="0" y="196.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-9">
+  <clipPath id="terminal-3216424293-line-9">
       <rect x="0" y="221.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-10">
+  <clipPath id="terminal-3216424293-line-10">
       <rect x="0" y="245.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-11">
+  <clipPath id="terminal-3216424293-line-11">
       <rect x="0" y="269.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-12">
+  <clipPath id="terminal-3216424293-line-12">
       <rect x="0" y="294.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-13">
+  <clipPath id="terminal-3216424293-line-13">
       <rect x="0" y="318.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-14">
+  <clipPath id="terminal-3216424293-line-14">
       <rect x="0" y="343.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-15">
+  <clipPath id="terminal-3216424293-line-15">
       <rect x="0" y="367.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-16">
+  <clipPath id="terminal-3216424293-line-16">
       <rect x="0" y="391.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-17">
+  <clipPath id="terminal-3216424293-line-17">
       <rect x="0" y="416.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-18">
+  <clipPath id="terminal-3216424293-line-18">
       <rect x="0" y="440.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-19">
+  <clipPath id="terminal-3216424293-line-19">
       <rect x="0" y="465.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-20">
+  <clipPath id="terminal-3216424293-line-20">
       <rect x="0" y="489.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-21">
+  <clipPath id="terminal-3216424293-line-21">
       <rect x="0" y="513.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3455460968-line-22">
+  <clipPath id="terminal-3216424293-line-22">
       <rect x="0" y="538.3" width="976" height="24.65"/>
               </clipPath>
       </defs>
   
-      <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/><text class="terminal-3455460968-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TooltipApp</text>
+      <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/><text class="terminal-3216424293-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">TooltipApp</text>
               <g transform="translate(26,22)">
               <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
               <circle cx="22" cy="0" r="7" fill="#febc2e"/>
               <circle cx="44" cy="0" r="7" fill="#28c840"/>
               </g>
           
-      <g transform="translate(9, 41)" clip-path="url(#terminal-3455460968-clip-terminal)">
-      <rect fill="#1e1e1e" x="0" y="1.5" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="36.6" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="48.8" y="1.5" width="341.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="390.4" y="1.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="1.5" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="451.4" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="463.6" y="1.5" width="97.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="561.2" y="1.5" width="414.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="25.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#24292f" x="24.4" y="25.9" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="25.9" width="719.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="50.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#24292f" x="24.4" y="50.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#24292f" x="48.8" y="50.3" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#24292f" x="231.8" y="50.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="50.3" width="719.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="74.7" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#24292f" x="24.4" y="74.7" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="74.7" width="719.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="489.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="513.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="538.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="562.7" width="976" height="24.65" shape-rendering="crispEdges"/>
-      <g class="terminal-3455460968-matrix">
-      <text class="terminal-3455460968-r1" x="0" y="20" textLength="36.6" clip-path="url(#terminal-3455460968-line-0)">━━━</text><text class="terminal-3455460968-r2" x="36.6" y="20" textLength="12.2" clip-path="url(#terminal-3455460968-line-0)">╺</text><text class="terminal-3455460968-r2" x="48.8" y="20" textLength="341.6" clip-path="url(#terminal-3455460968-line-0)">━━━━━━━━━━━━━━━━━━━━━━━━━━━━</text><text class="terminal-3455460968-r4" x="414.8" y="20" textLength="36.6" clip-path="url(#terminal-3455460968-line-0)">10%</text><text class="terminal-3455460968-r4" x="463.6" y="20" textLength="97.6" clip-path="url(#terminal-3455460968-line-0)">--:--:--</text><text class="terminal-3455460968-r3" x="976" y="20" textLength="12.2" clip-path="url(#terminal-3455460968-line-0)">
-  </text><text class="terminal-3455460968-r3" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-3455460968-line-1)">
-  </text><text class="terminal-3455460968-r5" x="48.8" y="68.8" textLength="183" clip-path="url(#terminal-3455460968-line-2)">Hello,&#160;Tooltip!</text><text class="terminal-3455460968-r3" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-3455460968-line-2)">
-  </text><text class="terminal-3455460968-r3" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-3455460968-line-3)">
-  </text><text class="terminal-3455460968-r3" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-3455460968-line-4)">
-  </text><text class="terminal-3455460968-r3" x="976" y="142" textLength="12.2" clip-path="url(#terminal-3455460968-line-5)">
-  </text><text class="terminal-3455460968-r3" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-3455460968-line-6)">
-  </text><text class="terminal-3455460968-r3" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-3455460968-line-7)">
-  </text><text class="terminal-3455460968-r3" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-3455460968-line-8)">
-  </text><text class="terminal-3455460968-r3" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-3455460968-line-9)">
-  </text><text class="terminal-3455460968-r3" x="976" y="264" textLength="12.2" clip-path="url(#terminal-3455460968-line-10)">
-  </text><text class="terminal-3455460968-r3" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-3455460968-line-11)">
-  </text><text class="terminal-3455460968-r3" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-3455460968-line-12)">
-  </text><text class="terminal-3455460968-r3" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-3455460968-line-13)">
-  </text><text class="terminal-3455460968-r3" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-3455460968-line-14)">
-  </text><text class="terminal-3455460968-r3" x="976" y="386" textLength="12.2" clip-path="url(#terminal-3455460968-line-15)">
-  </text><text class="terminal-3455460968-r3" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-3455460968-line-16)">
-  </text><text class="terminal-3455460968-r3" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-3455460968-line-17)">
-  </text><text class="terminal-3455460968-r3" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-3455460968-line-18)">
-  </text><text class="terminal-3455460968-r3" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-3455460968-line-19)">
-  </text><text class="terminal-3455460968-r3" x="976" y="508" textLength="12.2" clip-path="url(#terminal-3455460968-line-20)">
-  </text><text class="terminal-3455460968-r3" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-3455460968-line-21)">
-  </text><text class="terminal-3455460968-r3" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-3455460968-line-22)">
+      <g transform="translate(9, 41)" clip-path="url(#terminal-3216424293-clip-terminal)">
+      <rect fill="#1e1e1e" x="0" y="1.5" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="36.6" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="48.8" y="1.5" width="341.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="390.4" y="1.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="414.8" y="1.5" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="451.4" y="1.5" width="524.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="25.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#24292f" x="24.4" y="25.9" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="25.9" width="719.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="50.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#24292f" x="24.4" y="50.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#24292f" x="48.8" y="50.3" width="183" height="24.65" shape-rendering="crispEdges"/><rect fill="#24292f" x="231.8" y="50.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="50.3" width="719.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="74.7" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#24292f" x="24.4" y="74.7" width="231.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="256.2" y="74.7" width="719.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="489.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="513.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="538.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="562.7" width="976" height="24.65" shape-rendering="crispEdges"/>
+      <g class="terminal-3216424293-matrix">
+      <text class="terminal-3216424293-r1" x="0" y="20" textLength="36.6" clip-path="url(#terminal-3216424293-line-0)">━━━</text><text class="terminal-3216424293-r2" x="36.6" y="20" textLength="12.2" clip-path="url(#terminal-3216424293-line-0)">╺</text><text class="terminal-3216424293-r2" x="48.8" y="20" textLength="341.6" clip-path="url(#terminal-3216424293-line-0)">━━━━━━━━━━━━━━━━━━━━━━━━━━━━</text><text class="terminal-3216424293-r4" x="414.8" y="20" textLength="36.6" clip-path="url(#terminal-3216424293-line-0)">10%</text><text class="terminal-3216424293-r3" x="976" y="20" textLength="12.2" clip-path="url(#terminal-3216424293-line-0)">
+  </text><text class="terminal-3216424293-r3" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-3216424293-line-1)">
+  </text><text class="terminal-3216424293-r5" x="48.8" y="68.8" textLength="183" clip-path="url(#terminal-3216424293-line-2)">Hello,&#160;Tooltip!</text><text class="terminal-3216424293-r3" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-3216424293-line-2)">
+  </text><text class="terminal-3216424293-r3" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-3216424293-line-3)">
+  </text><text class="terminal-3216424293-r3" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-3216424293-line-4)">
+  </text><text class="terminal-3216424293-r3" x="976" y="142" textLength="12.2" clip-path="url(#terminal-3216424293-line-5)">
+  </text><text class="terminal-3216424293-r3" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-3216424293-line-6)">
+  </text><text class="terminal-3216424293-r3" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-3216424293-line-7)">
+  </text><text class="terminal-3216424293-r3" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-3216424293-line-8)">
+  </text><text class="terminal-3216424293-r3" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-3216424293-line-9)">
+  </text><text class="terminal-3216424293-r3" x="976" y="264" textLength="12.2" clip-path="url(#terminal-3216424293-line-10)">
+  </text><text class="terminal-3216424293-r3" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-3216424293-line-11)">
+  </text><text class="terminal-3216424293-r3" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-3216424293-line-12)">
+  </text><text class="terminal-3216424293-r3" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-3216424293-line-13)">
+  </text><text class="terminal-3216424293-r3" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-3216424293-line-14)">
+  </text><text class="terminal-3216424293-r3" x="976" y="386" textLength="12.2" clip-path="url(#terminal-3216424293-line-15)">
+  </text><text class="terminal-3216424293-r3" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-3216424293-line-16)">
+  </text><text class="terminal-3216424293-r3" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-3216424293-line-17)">
+  </text><text class="terminal-3216424293-r3" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-3216424293-line-18)">
+  </text><text class="terminal-3216424293-r3" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-3216424293-line-19)">
+  </text><text class="terminal-3216424293-r3" x="976" y="508" textLength="12.2" clip-path="url(#terminal-3216424293-line-20)">
+  </text><text class="terminal-3216424293-r3" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-3216424293-line-21)">
+  </text><text class="terminal-3216424293-r3" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-3216424293-line-22)">
   </text>
       </g>
       </g>
diff --git a/tests/snapshot_tests/snapshot_apps/recompose.py b/tests/snapshot_tests/snapshot_apps/recompose.py
index 8275289eeb..f0e5909f95 100644
--- a/tests/snapshot_tests/snapshot_apps/recompose.py
+++ b/tests/snapshot_tests/snapshot_apps/recompose.py
@@ -29,7 +29,7 @@ class Progress(Horizontal):
     progress = reactive(0, recompose=True)
 
     def compose(self) -> ComposeResult:
-        bar = ProgressBar(100)
+        bar = ProgressBar(100, show_eta=False)
         bar.progress = self.progress
         yield bar
 
diff --git a/tests/snapshot_tests/snapshot_apps/tooltips.py b/tests/snapshot_tests/snapshot_apps/tooltips.py
index bc55542d71..6c9ca8aea5 100644
--- a/tests/snapshot_tests/snapshot_apps/tooltips.py
+++ b/tests/snapshot_tests/snapshot_apps/tooltips.py
@@ -4,7 +4,7 @@
 
 class TooltipApp(App[None]):
     def compose(self) -> ComposeResult:
-        progress_bar = ProgressBar(100)
+        progress_bar = ProgressBar(100, show_eta=False)
         progress_bar.advance(10)
         progress_bar.tooltip = "Hello, Tooltip!"
         yield progress_bar
diff --git a/tests/test_eta.py b/tests/test_eta.py
new file mode 100644
index 0000000000..c91903293b
--- /dev/null
+++ b/tests/test_eta.py
@@ -0,0 +1,56 @@
+from textual.eta import ETA
+
+
+def test_basics() -> None:
+    eta = ETA()
+    eta.add_sample(1.0, 1.0)
+    assert eta.first_sample == (0, 0)
+    assert eta.last_sample == (1.0, 1.0)
+    assert len(eta._samples) == 2
+    repr(eta)
+
+
+def test_speed() -> None:
+    eta = ETA()
+    # One sample is not enough to determine speed
+    assert eta.speed is None
+    eta.add_sample(1.0, 0.5)
+    assert eta.speed == 0.5
+
+    # Check reset
+    eta.reset()
+    assert eta.speed is None
+    eta.add_sample(0.0, 0.0)
+    assert eta.speed is None
+    eta.add_sample(1.0, 0.5)
+    assert eta.speed == 0.5
+
+    # Check backwards
+    eta.add_sample(2.0, 0.0)
+    assert eta.speed is None
+    eta.add_sample(3.0, 1.0)
+    assert eta.speed == 1.0
+
+
+def test_get_progress_at() -> None:
+    eta = ETA()
+    eta.add_sample(1, 2)
+    # Check interpolation works
+    assert eta._get_progress_at(0) == (0, 0)
+    assert eta._get_progress_at(0.1) == (0.1, 0.2)
+    assert eta._get_progress_at(0.5) == (0.5, 1.0)
+
+
+def test_eta():
+    eta = ETA(estimation_period=2, extrapolate_period=5)
+    eta.add_sample(1, 0.1)
+    assert eta.speed == 0.1
+    assert eta.get_eta(1) == 9
+    assert eta.get_eta(2) == 8
+    assert eta.get_eta(3) == 7
+    assert eta.get_eta(4) == 6
+    assert eta.get_eta(5) == 5
+    # After 5 seconds (extrapolate_period), eta won't update
+    assert eta.get_eta(6) == 4
+    assert eta.get_eta(7) == 4
+    assert eta.get_eta(8) == 4
diff --git a/tests/test_progress_bar.py b/tests/test_progress_bar.py
index bc7f799196..0e9663bf30 100644
--- a/tests/test_progress_bar.py
+++ b/tests/test_progress_bar.py
@@ -48,11 +48,9 @@ def test_progress_overflow():
     pb = ProgressBar(total=100)
 
     pb.advance(999_999)
-    assert pb.progress == 100
     assert pb.percentage == 1
 
     pb.update(total=50)
-    assert pb.progress == 50
     assert pb.percentage == 1
 
 
@@ -60,7 +58,6 @@ def test_progress_underflow():
     pb = ProgressBar(total=100)
 
     pb.advance(-999_999)
-    assert pb.progress == 0
     assert pb.percentage == 0
 
 


Code review comments:
diff_hunk: @@ -0,0 +1,126 @@
+from __future__ import annotations
+
+import bisect
+from math import ceil
+from time import monotonic
+
+import rich.repr
+
+
+@rich.repr.auto(angular=True)
+class ETA:
+    """Calculate speed and estimate time to arrival."""
+
+    def __init__(
+        self, estimation_period: float = 60, extrapolate_period: float = 30
+    ) -> None:
+        """Create an ETA.
+
+        Args:
+            estimation_period: Period in seconds, used to calculate speed.
+            extrapolate_period: Maximum number of seconds used to estimate progress after last sample.
+        """
+        self.estimation_period = estimation_period
+        self.max_extrapolate = extrapolate_period
+        self._samples: list[tuple[float, float]] = [(0.0, 0.0)]
+        self._add_count = 0
+
+    def __rich_repr__(self) -> rich.repr.Result:
+        yield "speed", self.speed
+        yield "eta", self.get_eta(monotonic())
+
+    @property
+    def first_sample(self) -> tuple[float, float]:
+        """First sample, or `None` if no samples."""
+        assert self._samples, "Assumes samples not empty"
+        return self._samples[0]
+
+    @property
+    def last_sample(self) -> tuple[float, float]:
+        """Last sample, or `None` if no samples."""
path: src/textual/eta.py
text: @user1:
The docstring here doesn't match the typing.