Repository: Textualize/textual

Issue:
Input auto-complete
We need a way to suggest auto-complete for the Input widget.

The auto-complete I'm thinking of is when you start typing and a the rest of the word or phrase appears in front of the cursor. Something like this:

You hit `T` and the text `extualize` appears with a different style in front of the cursor:

T*extualize*

Hitting `e` adds *xtualize*

Te*xtualize*

Pressing escape cancels the auto complete, pressing return accept it.

## The tricky part

The obvious solution would be to have a callback that takes the text in the input, and returns a potential auto-complete. However, we want this to be asynchronous. i.e. the callback should be able to do non-blocking work without making typing slow.

The probably probably requires the use of workers. Please discuss with @willmcgugan re the proposed solution.


Discussion:
Usually with this kind of interface pressing the right arrow when the cursor is at the end accepts it. Pressing return won't work because it would be unclear whether the user is trying to submit input or accept the autocomplete.
@darrenburns I do find that annoying. But return to accept the auto-complete seems to be common. If you don't want to accept the auto-complete, you can press escape to dismiss it first.
> The auto-complete I'm thinking of is when you start typing and a the rest of the word or phrase appears in front of the cursor

Hope you don't mind me sticking my nose in... might this also allow a way showing a list of possible completions? This can be  helpful when you might not know _exactly_ what you're looking for.

(I'm aware there's some crossover with darren's personal autocomplete project here, so don't want to step on any toes!)
I think we will have a builtin version of Darren's autocomplete at some point. Which might extend this feature. But its also nice to have this in isolation.
Consider the quick typing (mindless) user looking elsewhere doing data entry.

If it's a closed set of options, it's fine to have `enter` to accept, if it's free text, what if they did try and write 'te'? There should be a canonical way for them to input exactly that, regardless of possible completions, I guess making sure to hit `escape` works for that... I think you mention insipration by vue etc. how do they handle this?
I think we kind of _have_ to accept auto-completion on <kbd>Enter</kbd>. (Independently of also allowing <kbd>right</kbd> or <kbd>Tab</kbd> to accept the suggestion or not.)

Suppose we _didn't_.
Someone opens an app with an input, starts typing, and sees an auto-completion suggestion.
The user presses <kbd>Enter</kbd> to accept the suggestion.
Instead of accepting it, the user unintentionally triggered the message `Input.Submitted` which may have consequences that are difficult to recover from just because the user used the wrong keybinding.

The other side of this story is "the user wanted to submit _exactly_ what was typed, even though there is a suggestion showing, and upon pressing <kbd>Enter</kbd> the suggestion is accepted instead of submitting the input".
This is much easier to recover from because the user only has to delete the unwanted characters.
@rodrigogiraoserrao That's user error. The auto-completion is dimmed out, so it doesn't appear to the user that pressing enter will submit the entire completion suggestion.

This is how it works in both `zsh` and `fish` and many other shells. `prompt-toolkit` also takes the exact same approach.

<img width="572" alt="image" src="https://github.com/Textualize/textual/assets/5740731/31a31fb5-7f60-449c-8c0e-d1f30a1bf70b">

If I press enter in the image above, it submits "env TEX". If I press right or tab it fills in the remainder of the command. Additionally if I press Option+right, it fills a single word.

To me, if we implement it in the way suggested earlier it's just doing it the opposite of how these things generally work in terminals and is really counterintuitive. I've never seen the approach suggested in this thread anywhere before personally and it doesn't feel like it'd be intuitive for terminal users.

**Side note:** I like how prompt-toolkit calls this feature "autosuggest" and reserves "autocomplete" for its autocomplete dropdown feature: https://python-prompt-toolkit.readthedocs.io/en/master/pages/asking_for_input.html#auto-suggestion

That makes more sense to me. I've always hated return to auto-complete. Often leads me to entering things I didn't intend to.

Resolving patch:
diff --git a/src/textual/suggester.py b/src/textual/suggester.py
new file mode 100644
--- /dev/null
+++ b/src/textual/suggester.py
@@ -0,0 +1,138 @@
+from __future__ import annotations
+
+from abc import ABC, abstractmethod
+from dataclasses import dataclass
+from typing import Iterable
+
+from ._cache import LRUCache
+from .dom import DOMNode
+from .message import Message
+
+
+@dataclass
+class SuggestionReady(Message):
+    """Sent when a completion suggestion is ready."""
+
+    value: str
+    """The value to which the suggestion is for."""
+    suggestion: str
+    """The string suggestion."""
+
+
+class Suggester(ABC):
+    """Defines how widgets generate completion suggestions.
+
+    To define a custom suggester, subclass `Suggester` and implement the async method
+    `get_suggestion`.
+    See [`SuggestFromList`][textual.suggester.SuggestFromList] for an example.
+    """
+
+    cache: LRUCache[str, str | None] | None
+    """Suggestion cache, if used."""
+
+    def __init__(self, *, use_cache: bool = True, case_sensitive: bool = False) -> None:
+        """Create a suggester object.
+
+        Args:
+            use_cache: Whether to cache suggestion results.
+            case_sensitive: Whether suggestions are case sensitive or not.
+                If they are not, incoming values are casefolded before generating
+                the suggestion.
+        """
+        self.cache = LRUCache(1024) if use_cache else None
+        self.case_sensitive = case_sensitive
+
+    async def _get_suggestion(self, requester: DOMNode, value: str) -> None:
+        """Used by widgets to get completion suggestions.
+
+        Note:
+            When implementing custom suggesters, this method does not need to be
+            overridden.
+
+        Args:
+            requester: The message target that requested a suggestion.
+            value: The current value to complete.
+        """
+
+        normalized_value = value if self.case_sensitive else value.casefold()
+        if self.cache is None or normalized_value not in self.cache:
+            suggestion = await self.get_suggestion(normalized_value)
+            if self.cache is not None:
+                self.cache[normalized_value] = suggestion
+        else:
+            suggestion = self.cache[normalized_value]
+
+        if suggestion is None:
+            return
+        requester.post_message(SuggestionReady(value, suggestion))
+
+    @abstractmethod
+    async def get_suggestion(self, value: str) -> str | None:
+        """Try to get a completion suggestion for the given input value.
+
+        Custom suggesters should implement this method.
+
+        Note:
+            The value argument will be casefolded if `self.case_sensitive` is `False`.
+
+        Note:
+            If your implementation is not deterministic, you may need to disable caching.
+
+        Args:
+            value: The current value of the requester widget.
+
+        Returns:
+            A valid suggestion or `None`.
+        """
+        pass
+
+
+class SuggestFromList(Suggester):
+    """Give completion suggestions based on a fixed list of options.
+
+    Example:
+        ```py
+        countries = ["England", "Scotland", "Portugal", "Spain", "France"]
+
+        class MyApp(App[None]):
+            def compose(self) -> ComposeResult:
+                yield Input(suggester=SuggestFromList(countries, case_sensitive=False))
+        ```
+
+        If the user types ++p++ inside the input widget, a completion suggestion
+        for `"Portugal"` appears.
+    """
+
+    def __init__(
+        self, suggestions: Iterable[str], *, case_sensitive: bool = True
+    ) -> None:
+        """Creates a suggester based off of a given iterable of possibilities.
+
+        Args:
+            suggestions: Valid suggestions sorted by decreasing priority.
+            case_sensitive: Whether suggestions are computed in a case sensitive manner
+                or not. The values provided in the argument `suggestions` represent the
+                canonical representation of the completions and they will be suggested
+                with that same casing.
+        """
+        super().__init__(case_sensitive=case_sensitive)
+        self._suggestions = list(suggestions)
+        self._for_comparison = (
+            self._suggestions
+            if self.case_sensitive
+            else [suggestion.casefold() for suggestion in self._suggestions]
+        )
+
+    async def get_suggestion(self, value: str) -> str | None:
+        """Gets a completion from the given possibilities.
+
+        Args:
+            value: The current value.
+
+        Returns:
+            A valid completion suggestion or `None`.
+        """
+        for idx, suggestion in enumerate(self._for_comparison):
+            if suggestion.startswith(value):
+                return self._suggestions[idx]
+        return None
diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py
--- a/src/textual/widgets/_input.py
+++ b/src/textual/widgets/_input.py
@@ -17,7 +17,8 @@
 from ..geometry import Size
 from ..message import Message
 from ..reactive import reactive
-from ..validation import Failure, ValidationResult, Validator
+from ..suggester import Suggester, SuggestionReady
+from ..validation import ValidationResult, Validator
 from ..widget import Widget
 
 
@@ -33,13 +34,26 @@ def __rich_console__(
     ) -> "RenderResult":
         input = self.input
         result = input._value
-        if input._cursor_at_end:
-            result.pad_right(1)
-        cursor_style = input.get_component_rich_style("input--cursor")
+        width = input.content_size.width
+
+        # Add the completion with a faded style.
+        value = input.value
+        value_length = len(value)
+        suggestion = input._suggestion
+        show_suggestion = len(suggestion) > value_length
+        if show_suggestion:
+            result += Text(
+                suggestion[value_length:],
+                input.get_component_rich_style("input--suggestion"),
+            )
+
         if self.cursor_visible and input.has_focus:
+            if not show_suggestion and input._cursor_at_end:
+                result.pad_right(1)
+            cursor_style = input.get_component_rich_style("input--cursor")
             cursor = input.cursor_position
             result.stylize(cursor_style, cursor, cursor + 1)
-        width = input.content_size.width
+
         segments = list(result.render(console))
         line_length = Segment.get_line_length(segments)
         if line_length < width:
@@ -82,7 +96,7 @@ class Input(Widget, can_focus=True):
     | :- | :- |
     | left | Move the cursor left. |
     | ctrl+left | Move the cursor one word to the left. |
-    | right | Move the cursor right. |
+    | right | Move the cursor right or accept the completion suggestion. |
     | ctrl+right | Move the cursor one word to the right. |
     | backspace | Delete the character to the left of the cursor. |
     | home,ctrl+a | Go to the beginning of the input. |
@@ -95,12 +109,17 @@ class Input(Widget, can_focus=True):
     | ctrl+k | Delete everything to the right of the cursor. |
     """
 
-    COMPONENT_CLASSES: ClassVar[set[str]] = {"input--cursor", "input--placeholder"}
+    COMPONENT_CLASSES: ClassVar[set[str]] = {
+        "input--cursor",
+        "input--placeholder",
+        "input--suggestion",
+    }
     """
     | Class | Description |
     | :- | :- |
     | `input--cursor` | Target the cursor. |
     | `input--placeholder` | Target the placeholder text (when it exists). |
+    | `input--suggestion` | Target the auto-completion suggestion (when it exists). |
     """
 
     DEFAULT_CSS = """
@@ -121,7 +140,7 @@ class Input(Widget, can_focus=True):
         color: $text;
         text-style: reverse;
     }
-    Input>.input--placeholder {
+    Input>.input--placeholder, Input>.input--suggestion {
         color: $text-disabled;
     }
     Input.-invalid {
@@ -143,6 +162,10 @@ class Input(Widget, can_focus=True):
     _cursor_visible = reactive(True)
     password = reactive(False)
     max_size: reactive[int | None] = reactive(None)
+    suggester: Suggester | None
+    """The suggester used to provide completions as the user types."""
+    _suggestion = reactive("")
+    """A completion suggestion for the current value in the input."""
 
     @dataclass
     class Changed(Message):
@@ -195,6 +218,8 @@ def __init__(
         placeholder: str = "",
         highlighter: Highlighter | None = None,
         password: bool = False,
+        *,
+        suggester: Suggester | None = None,
         validators: Validator | Iterable[Validator] | None = None,
         name: str | None = None,
         id: str | None = None,
@@ -207,7 +232,9 @@ def __init__(
             value: An optional default value for the input.
             placeholder: Optional placeholder text for the input.
             highlighter: An optional highlighter for the input.
-            password: Flag to say if the field should obfuscate its content. Default is `False`.
+            password: Flag to say if the field should obfuscate its content.
+            suggester: [`Suggester`][textual.suggester.Suggester] associated with this
+                input instance.
             validators: An iterable of validators that the Input value will be checked against.
             name: Optional name for the input widget.
             id: Optional ID for the widget.
@@ -220,6 +247,7 @@ def __init__(
         self.placeholder = placeholder
         self.highlighter = highlighter
         self.password = password
+        self.suggester = suggester
         # Ensure we always end up with an Iterable of validators
         if isinstance(validators, Validator):
             self.validators: list[Validator] = [validators]
@@ -272,6 +300,9 @@ def watch_cursor_position(self, cursor_position: int) -> None:
             self.view_position = self.view_position
 
     async def watch_value(self, value: str) -> None:
+        self._suggestion = ""
+        if self.suggester and value:
+            self.run_worker(self.suggester._get_suggestion(self, value))
         if self.styles.auto_dimensions:
             self.refresh(layout=True)
 
@@ -402,13 +433,18 @@ async def _on_click(self, event: events.Click) -> None:
         else:
             self.cursor_position = len(self.value)
 
+    async def _on_suggestion_ready(self, event: SuggestionReady) -> None:
+        """Handle suggestion messages and set the suggestion when relevant."""
+        if event.value == self.value:
+            self._suggestion = event.suggestion
+
     def insert_text_at_cursor(self, text: str) -> None:
         """Insert new text at the cursor, move the cursor to the end of the new text.
 
         Args:
             text: New text to insert.
         """
-        if self.cursor_position > len(self.value):
+        if self.cursor_position >= len(self.value):
             self.value += text
             self.cursor_position = len(self.value)
         else:
@@ -423,8 +459,12 @@ def action_cursor_left(self) -> None:
         self.cursor_position -= 1
 
     def action_cursor_right(self) -> None:
-        """Move the cursor one position to the right."""
-        self.cursor_position += 1
+        """Accept an auto-completion or move the cursor one position to the right."""
+        if self._cursor_at_end and self._suggestion:
+            self.value = self._suggestion
+            self.cursor_position = len(self.value)
+        else:
+            self.cursor_position += 1
 
     def action_home(self) -> None:
         """Move the cursor to the start of the input."""


Test patch:
diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
--- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
+++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
@@ -732,134 +732,134 @@
           font-weight: 700;
       }
   
-      .terminal-1256256702-matrix {
+      .terminal-1939862718-matrix {
           font-family: Fira Code, monospace;
           font-size: 20px;
           line-height: 24.4px;
           font-variant-east-asian: full-width;
       }
   
-      .terminal-1256256702-title {
+      .terminal-1939862718-title {
           font-size: 18px;
           font-weight: bold;
           font-family: arial;
       }
   
-      .terminal-1256256702-r1 { fill: #1e1e1e }
-  .terminal-1256256702-r2 { fill: #161616 }
-  .terminal-1256256702-r3 { fill: #c5c8c6 }
-  .terminal-1256256702-r4 { fill: #7c7c7c }
-  .terminal-1256256702-r5 { fill: #e1e1e1 }
+      .terminal-1939862718-r1 { fill: #1e1e1e }
+  .terminal-1939862718-r2 { fill: #161616 }
+  .terminal-1939862718-r3 { fill: #c5c8c6 }
+  .terminal-1939862718-r4 { fill: #7c7c7c }
+  .terminal-1939862718-r5 { fill: #e1e1e1 }
       </style>
   
       <defs>
-      <clipPath id="terminal-1256256702-clip-terminal">
+      <clipPath id="terminal-1939862718-clip-terminal">
         <rect x="0" y="0" width="975.0" height="584.5999999999999" />
       </clipPath>
-      <clipPath id="terminal-1256256702-line-0">
+      <clipPath id="terminal-1939862718-line-0">
       <rect x="0" y="1.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-1">
+  <clipPath id="terminal-1939862718-line-1">
       <rect x="0" y="25.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-2">
+  <clipPath id="terminal-1939862718-line-2">
       <rect x="0" y="50.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-3">
+  <clipPath id="terminal-1939862718-line-3">
       <rect x="0" y="74.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-4">
+  <clipPath id="terminal-1939862718-line-4">
       <rect x="0" y="99.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-5">
+  <clipPath id="terminal-1939862718-line-5">
       <rect x="0" y="123.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-6">
+  <clipPath id="terminal-1939862718-line-6">
       <rect x="0" y="147.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-7">
+  <clipPath id="terminal-1939862718-line-7">
       <rect x="0" y="172.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-8">
+  <clipPath id="terminal-1939862718-line-8">
       <rect x="0" y="196.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-9">
+  <clipPath id="terminal-1939862718-line-9">
       <rect x="0" y="221.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-10">
+  <clipPath id="terminal-1939862718-line-10">
       <rect x="0" y="245.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-11">
+  <clipPath id="terminal-1939862718-line-11">
       <rect x="0" y="269.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-12">
+  <clipPath id="terminal-1939862718-line-12">
       <rect x="0" y="294.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-13">
+  <clipPath id="terminal-1939862718-line-13">
       <rect x="0" y="318.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-14">
+  <clipPath id="terminal-1939862718-line-14">
       <rect x="0" y="343.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-15">
+  <clipPath id="terminal-1939862718-line-15">
       <rect x="0" y="367.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-16">
+  <clipPath id="terminal-1939862718-line-16">
       <rect x="0" y="391.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-17">
+  <clipPath id="terminal-1939862718-line-17">
       <rect x="0" y="416.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-18">
+  <clipPath id="terminal-1939862718-line-18">
       <rect x="0" y="440.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-19">
+  <clipPath id="terminal-1939862718-line-19">
       <rect x="0" y="465.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-20">
+  <clipPath id="terminal-1939862718-line-20">
       <rect x="0" y="489.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-21">
+  <clipPath id="terminal-1939862718-line-21">
       <rect x="0" y="513.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-1256256702-line-22">
+  <clipPath id="terminal-1939862718-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-1256256702-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">BlurApp</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-1939862718-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">BlurApp</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-1256256702-clip-terminal)">
-      <rect fill="#161616" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="12.2" y="1.5" width="951.6" 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="#161616" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="12.2" y="25.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="36.6" y="25.9" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="85.4" y="25.9" width="854" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="939.4" y="25.9" width="24.4" 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="#161616" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="12.2" y="50.3" width="951.6" 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="976" 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-1256256702-matrix">
-      <text class="terminal-1256256702-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-1256256702-line-0)">▊</text><text class="terminal-1256256702-r2" x="12.2" y="20" textLength="951.6" clip-path="url(#terminal-1256256702-line-0)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-1256256702-r2" x="963.8" y="20" textLength="12.2" clip-path="url(#terminal-1256256702-line-0)">▎</text><text class="terminal-1256256702-r3" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1256256702-line-0)">
-  </text><text class="terminal-1256256702-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-1256256702-line-1)">▊</text><text class="terminal-1256256702-r4" x="36.6" y="44.4" textLength="48.8" clip-path="url(#terminal-1256256702-line-1)">foo&#160;</text><text class="terminal-1256256702-r2" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-1256256702-line-1)">▎</text><text class="terminal-1256256702-r3" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1256256702-line-1)">
-  </text><text class="terminal-1256256702-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-1256256702-line-2)">▊</text><text class="terminal-1256256702-r2" x="12.2" y="68.8" textLength="951.6" clip-path="url(#terminal-1256256702-line-2)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-1256256702-r2" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1256256702-line-2)">▎</text><text class="terminal-1256256702-r3" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1256256702-line-2)">
-  </text><text class="terminal-1256256702-r3" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1256256702-line-3)">
-  </text><text class="terminal-1256256702-r3" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1256256702-line-4)">
-  </text><text class="terminal-1256256702-r3" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1256256702-line-5)">
-  </text><text class="terminal-1256256702-r3" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1256256702-line-6)">
-  </text><text class="terminal-1256256702-r3" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1256256702-line-7)">
-  </text><text class="terminal-1256256702-r3" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1256256702-line-8)">
-  </text><text class="terminal-1256256702-r3" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1256256702-line-9)">
-  </text><text class="terminal-1256256702-r3" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1256256702-line-10)">
-  </text><text class="terminal-1256256702-r3" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1256256702-line-11)">
-  </text><text class="terminal-1256256702-r3" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1256256702-line-12)">
-  </text><text class="terminal-1256256702-r3" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1256256702-line-13)">
-  </text><text class="terminal-1256256702-r3" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1256256702-line-14)">
-  </text><text class="terminal-1256256702-r3" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1256256702-line-15)">
-  </text><text class="terminal-1256256702-r3" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1256256702-line-16)">
-  </text><text class="terminal-1256256702-r3" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1256256702-line-17)">
-  </text><text class="terminal-1256256702-r3" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1256256702-line-18)">
-  </text><text class="terminal-1256256702-r3" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1256256702-line-19)">
-  </text><text class="terminal-1256256702-r3" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1256256702-line-20)">
-  </text><text class="terminal-1256256702-r3" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1256256702-line-21)">
-  </text><text class="terminal-1256256702-r3" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1256256702-line-22)">
+      <g transform="translate(9, 41)" clip-path="url(#terminal-1939862718-clip-terminal)">
+      <rect fill="#161616" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="12.2" y="1.5" width="951.6" 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="#161616" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="12.2" y="25.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="36.6" y="25.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="73.2" y="25.9" width="866.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="939.4" y="25.9" width="24.4" 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="#161616" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#222222" x="12.2" y="50.3" width="951.6" 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="976" 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-1939862718-matrix">
+      <text class="terminal-1939862718-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-1939862718-line-0)">▊</text><text class="terminal-1939862718-r2" x="12.2" y="20" textLength="951.6" clip-path="url(#terminal-1939862718-line-0)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-1939862718-r2" x="963.8" y="20" textLength="12.2" clip-path="url(#terminal-1939862718-line-0)">▎</text><text class="terminal-1939862718-r3" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1939862718-line-0)">
+  </text><text class="terminal-1939862718-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-1939862718-line-1)">▊</text><text class="terminal-1939862718-r4" x="36.6" y="44.4" textLength="36.6" clip-path="url(#terminal-1939862718-line-1)">foo</text><text class="terminal-1939862718-r2" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-1939862718-line-1)">▎</text><text class="terminal-1939862718-r3" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1939862718-line-1)">
+  </text><text class="terminal-1939862718-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-1939862718-line-2)">▊</text><text class="terminal-1939862718-r2" x="12.2" y="68.8" textLength="951.6" clip-path="url(#terminal-1939862718-line-2)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-1939862718-r2" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1939862718-line-2)">▎</text><text class="terminal-1939862718-r3" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1939862718-line-2)">
+  </text><text class="terminal-1939862718-r3" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1939862718-line-3)">
+  </text><text class="terminal-1939862718-r3" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1939862718-line-4)">
+  </text><text class="terminal-1939862718-r3" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1939862718-line-5)">
+  </text><text class="terminal-1939862718-r3" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1939862718-line-6)">
+  </text><text class="terminal-1939862718-r3" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1939862718-line-7)">
+  </text><text class="terminal-1939862718-r3" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1939862718-line-8)">
+  </text><text class="terminal-1939862718-r3" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1939862718-line-9)">
+  </text><text class="terminal-1939862718-r3" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1939862718-line-10)">
+  </text><text class="terminal-1939862718-r3" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1939862718-line-11)">
+  </text><text class="terminal-1939862718-r3" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1939862718-line-12)">
+  </text><text class="terminal-1939862718-r3" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1939862718-line-13)">
+  </text><text class="terminal-1939862718-r3" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1939862718-line-14)">
+  </text><text class="terminal-1939862718-r3" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1939862718-line-15)">
+  </text><text class="terminal-1939862718-r3" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1939862718-line-16)">
+  </text><text class="terminal-1939862718-r3" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1939862718-line-17)">
+  </text><text class="terminal-1939862718-r3" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1939862718-line-18)">
+  </text><text class="terminal-1939862718-r3" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1939862718-line-19)">
+  </text><text class="terminal-1939862718-r3" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1939862718-line-20)">
+  </text><text class="terminal-1939862718-r3" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1939862718-line-21)">
+  </text><text class="terminal-1939862718-r3" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1939862718-line-22)">
   </text>
       </g>
       </g>
@@ -16654,135 +16654,295 @@
           font-weight: 700;
       }
   
-      .terminal-4205022328-matrix {
+      .terminal-596216952-matrix {
           font-family: Fira Code, monospace;
           font-size: 20px;
           line-height: 24.4px;
           font-variant-east-asian: full-width;
       }
   
-      .terminal-4205022328-title {
+      .terminal-596216952-title {
           font-size: 18px;
           font-weight: bold;
           font-family: arial;
       }
   
-      .terminal-4205022328-r1 { fill: #1e1e1e }
-  .terminal-4205022328-r2 { fill: #121212 }
-  .terminal-4205022328-r3 { fill: #c5c8c6 }
-  .terminal-4205022328-r4 { fill: #e2e2e2 }
-  .terminal-4205022328-r5 { fill: #0178d4 }
-  .terminal-4205022328-r6 { fill: #e1e1e1 }
+      .terminal-596216952-r1 { fill: #1e1e1e }
+  .terminal-596216952-r2 { fill: #121212 }
+  .terminal-596216952-r3 { fill: #c5c8c6 }
+  .terminal-596216952-r4 { fill: #e2e2e2 }
+  .terminal-596216952-r5 { fill: #0178d4 }
+  .terminal-596216952-r6 { fill: #e1e1e1 }
       </style>
   
       <defs>
-      <clipPath id="terminal-4205022328-clip-terminal">
+      <clipPath id="terminal-596216952-clip-terminal">
         <rect x="0" y="0" width="975.0" height="584.5999999999999" />
       </clipPath>
-      <clipPath id="terminal-4205022328-line-0">
+      <clipPath id="terminal-596216952-line-0">
       <rect x="0" y="1.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-1">
+  <clipPath id="terminal-596216952-line-1">
       <rect x="0" y="25.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-2">
+  <clipPath id="terminal-596216952-line-2">
       <rect x="0" y="50.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-3">
+  <clipPath id="terminal-596216952-line-3">
       <rect x="0" y="74.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-4">
+  <clipPath id="terminal-596216952-line-4">
       <rect x="0" y="99.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-5">
+  <clipPath id="terminal-596216952-line-5">
       <rect x="0" y="123.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-6">
+  <clipPath id="terminal-596216952-line-6">
       <rect x="0" y="147.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-7">
+  <clipPath id="terminal-596216952-line-7">
       <rect x="0" y="172.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-8">
+  <clipPath id="terminal-596216952-line-8">
       <rect x="0" y="196.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-9">
+  <clipPath id="terminal-596216952-line-9">
       <rect x="0" y="221.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-10">
+  <clipPath id="terminal-596216952-line-10">
       <rect x="0" y="245.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-11">
+  <clipPath id="terminal-596216952-line-11">
       <rect x="0" y="269.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-12">
+  <clipPath id="terminal-596216952-line-12">
       <rect x="0" y="294.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-13">
+  <clipPath id="terminal-596216952-line-13">
       <rect x="0" y="318.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-14">
+  <clipPath id="terminal-596216952-line-14">
       <rect x="0" y="343.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-15">
+  <clipPath id="terminal-596216952-line-15">
       <rect x="0" y="367.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-16">
+  <clipPath id="terminal-596216952-line-16">
       <rect x="0" y="391.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-17">
+  <clipPath id="terminal-596216952-line-17">
       <rect x="0" y="416.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-18">
+  <clipPath id="terminal-596216952-line-18">
       <rect x="0" y="440.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-19">
+  <clipPath id="terminal-596216952-line-19">
       <rect x="0" y="465.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-20">
+  <clipPath id="terminal-596216952-line-20">
       <rect x="0" y="489.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-21">
+  <clipPath id="terminal-596216952-line-21">
       <rect x="0" y="513.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-4205022328-line-22">
+  <clipPath id="terminal-596216952-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-4205022328-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">InputApp</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-596216952-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">InputApp</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-4205022328-clip-terminal)">
-      <rect fill="#121212" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="1.5" width="951.6" 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="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="25.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="25.9" width="85.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="122" y="25.9" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="939.4" y="25.9" width="24.4" 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="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="50.3" width="951.6" 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="#0178d4" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="74.7" width="951.6" 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="#0178d4" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="99.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="99.1" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#e1e1e1" x="97.6" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="109.8" y="99.1" width="829.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="939.4" y="99.1" width="24.4" 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="#0178d4" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="123.5" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="123.5" width="12.2" 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-4205022328-matrix">
-      <text class="terminal-4205022328-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-4205022328-line-0)">▊</text><text class="terminal-4205022328-r2" x="12.2" y="20" textLength="951.6" clip-path="url(#terminal-4205022328-line-0)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-4205022328-r2" x="963.8" y="20" textLength="12.2" clip-path="url(#terminal-4205022328-line-0)">▎</text><text class="terminal-4205022328-r3" x="976" y="20" textLength="12.2" clip-path="url(#terminal-4205022328-line-0)">
-  </text><text class="terminal-4205022328-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-4205022328-line-1)">▊</text><text class="terminal-4205022328-r4" x="36.6" y="44.4" textLength="85.4" clip-path="url(#terminal-4205022328-line-1)">Darren&#160;</text><text class="terminal-4205022328-r2" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-4205022328-line-1)">▎</text><text class="terminal-4205022328-r3" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-4205022328-line-1)">
-  </text><text class="terminal-4205022328-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-4205022328-line-2)">▊</text><text class="terminal-4205022328-r2" x="12.2" y="68.8" textLength="951.6" clip-path="url(#terminal-4205022328-line-2)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-4205022328-r2" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-4205022328-line-2)">▎</text><text class="terminal-4205022328-r3" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-4205022328-line-2)">
-  </text><text class="terminal-4205022328-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-4205022328-line-3)">▊</text><text class="terminal-4205022328-r5" x="12.2" y="93.2" textLength="951.6" clip-path="url(#terminal-4205022328-line-3)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-4205022328-r5" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-4205022328-line-3)">▎</text><text class="terminal-4205022328-r3" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-4205022328-line-3)">
-  </text><text class="terminal-4205022328-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-4205022328-line-4)">▊</text><text class="terminal-4205022328-r4" x="36.6" y="117.6" textLength="61" clip-path="url(#terminal-4205022328-line-4)">Burns</text><text class="terminal-4205022328-r5" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-4205022328-line-4)">▎</text><text class="terminal-4205022328-r3" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-4205022328-line-4)">
-  </text><text class="terminal-4205022328-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-4205022328-line-5)">▊</text><text class="terminal-4205022328-r5" x="12.2" y="142" textLength="951.6" clip-path="url(#terminal-4205022328-line-5)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-4205022328-r5" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-4205022328-line-5)">▎</text><text class="terminal-4205022328-r3" x="976" y="142" textLength="12.2" clip-path="url(#terminal-4205022328-line-5)">
-  </text><text class="terminal-4205022328-r3" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-4205022328-line-6)">
-  </text><text class="terminal-4205022328-r3" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-4205022328-line-7)">
-  </text><text class="terminal-4205022328-r3" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-4205022328-line-8)">
-  </text><text class="terminal-4205022328-r3" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-4205022328-line-9)">
-  </text><text class="terminal-4205022328-r3" x="976" y="264" textLength="12.2" clip-path="url(#terminal-4205022328-line-10)">
-  </text><text class="terminal-4205022328-r3" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-4205022328-line-11)">
-  </text><text class="terminal-4205022328-r3" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-4205022328-line-12)">
-  </text><text class="terminal-4205022328-r3" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-4205022328-line-13)">
-  </text><text class="terminal-4205022328-r3" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-4205022328-line-14)">
-  </text><text class="terminal-4205022328-r3" x="976" y="386" textLength="12.2" clip-path="url(#terminal-4205022328-line-15)">
-  </text><text class="terminal-4205022328-r3" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-4205022328-line-16)">
-  </text><text class="terminal-4205022328-r3" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-4205022328-line-17)">
-  </text><text class="terminal-4205022328-r3" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-4205022328-line-18)">
-  </text><text class="terminal-4205022328-r3" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-4205022328-line-19)">
-  </text><text class="terminal-4205022328-r3" x="976" y="508" textLength="12.2" clip-path="url(#terminal-4205022328-line-20)">
-  </text><text class="terminal-4205022328-r3" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-4205022328-line-21)">
-  </text><text class="terminal-4205022328-r3" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-4205022328-line-22)">
+      <g transform="translate(9, 41)" clip-path="url(#terminal-596216952-clip-terminal)">
+      <rect fill="#121212" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="1.5" width="951.6" 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="#121212" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="25.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="25.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="109.8" y="25.9" width="829.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="939.4" y="25.9" width="24.4" 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="#121212" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="50.3" width="951.6" 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="#0178d4" x="0" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="74.7" width="951.6" 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="#0178d4" x="0" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="99.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="99.1" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#e1e1e1" x="97.6" y="99.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="109.8" y="99.1" width="829.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="939.4" y="99.1" width="24.4" 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="#0178d4" x="0" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="123.5" width="951.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="963.8" y="123.5" width="12.2" 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-596216952-matrix">
+      <text class="terminal-596216952-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-596216952-line-0)">▊</text><text class="terminal-596216952-r2" x="12.2" y="20" textLength="951.6" clip-path="url(#terminal-596216952-line-0)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-596216952-r2" x="963.8" y="20" textLength="12.2" clip-path="url(#terminal-596216952-line-0)">▎</text><text class="terminal-596216952-r3" x="976" y="20" textLength="12.2" clip-path="url(#terminal-596216952-line-0)">
+  </text><text class="terminal-596216952-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-596216952-line-1)">▊</text><text class="terminal-596216952-r4" x="36.6" y="44.4" textLength="73.2" clip-path="url(#terminal-596216952-line-1)">Darren</text><text class="terminal-596216952-r2" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-596216952-line-1)">▎</text><text class="terminal-596216952-r3" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-596216952-line-1)">
+  </text><text class="terminal-596216952-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-596216952-line-2)">▊</text><text class="terminal-596216952-r2" x="12.2" y="68.8" textLength="951.6" clip-path="url(#terminal-596216952-line-2)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-596216952-r2" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-596216952-line-2)">▎</text><text class="terminal-596216952-r3" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-596216952-line-2)">
+  </text><text class="terminal-596216952-r1" x="0" y="93.2" textLength="12.2" clip-path="url(#terminal-596216952-line-3)">▊</text><text class="terminal-596216952-r5" x="12.2" y="93.2" textLength="951.6" clip-path="url(#terminal-596216952-line-3)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-596216952-r5" x="963.8" y="93.2" textLength="12.2" clip-path="url(#terminal-596216952-line-3)">▎</text><text class="terminal-596216952-r3" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-596216952-line-3)">
+  </text><text class="terminal-596216952-r1" x="0" y="117.6" textLength="12.2" clip-path="url(#terminal-596216952-line-4)">▊</text><text class="terminal-596216952-r4" x="36.6" y="117.6" textLength="61" clip-path="url(#terminal-596216952-line-4)">Burns</text><text class="terminal-596216952-r5" x="963.8" y="117.6" textLength="12.2" clip-path="url(#terminal-596216952-line-4)">▎</text><text class="terminal-596216952-r3" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-596216952-line-4)">
+  </text><text class="terminal-596216952-r1" x="0" y="142" textLength="12.2" clip-path="url(#terminal-596216952-line-5)">▊</text><text class="terminal-596216952-r5" x="12.2" y="142" textLength="951.6" clip-path="url(#terminal-596216952-line-5)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-596216952-r5" x="963.8" y="142" textLength="12.2" clip-path="url(#terminal-596216952-line-5)">▎</text><text class="terminal-596216952-r3" x="976" y="142" textLength="12.2" clip-path="url(#terminal-596216952-line-5)">
+  </text><text class="terminal-596216952-r3" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-596216952-line-6)">
+  </text><text class="terminal-596216952-r3" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-596216952-line-7)">
+  </text><text class="terminal-596216952-r3" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-596216952-line-8)">
+  </text><text class="terminal-596216952-r3" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-596216952-line-9)">
+  </text><text class="terminal-596216952-r3" x="976" y="264" textLength="12.2" clip-path="url(#terminal-596216952-line-10)">
+  </text><text class="terminal-596216952-r3" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-596216952-line-11)">
+  </text><text class="terminal-596216952-r3" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-596216952-line-12)">
+  </text><text class="terminal-596216952-r3" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-596216952-line-13)">
+  </text><text class="terminal-596216952-r3" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-596216952-line-14)">
+  </text><text class="terminal-596216952-r3" x="976" y="386" textLength="12.2" clip-path="url(#terminal-596216952-line-15)">
+  </text><text class="terminal-596216952-r3" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-596216952-line-16)">
+  </text><text class="terminal-596216952-r3" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-596216952-line-17)">
+  </text><text class="terminal-596216952-r3" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-596216952-line-18)">
+  </text><text class="terminal-596216952-r3" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-596216952-line-19)">
+  </text><text class="terminal-596216952-r3" x="976" y="508" textLength="12.2" clip-path="url(#terminal-596216952-line-20)">
+  </text><text class="terminal-596216952-r3" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-596216952-line-21)">
+  </text><text class="terminal-596216952-r3" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-596216952-line-22)">
+  </text>
+      </g>
+      </g>
+  </svg>
+  
+  '''
+# ---
+# name: test_input_suggestions
+  '''
+  <svg class="rich-terminal" viewBox="0 0 994 635.5999999999999" xmlns="http://www.w3.org/2000/svg">
+      <!-- Generated with Rich https://www.textualize.io -->
+      <style>
+  
+      @font-face {
+          font-family: "Fira Code";
+          src: local("FiraCode-Regular"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+          font-style: normal;
+          font-weight: 400;
+      }
+      @font-face {
+          font-family: "Fira Code";
+          src: local("FiraCode-Bold"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+          font-style: bold;
+          font-weight: 700;
+      }
+  
+      .terminal-1319604136-matrix {
+          font-family: Fira Code, monospace;
+          font-size: 20px;
+          line-height: 24.4px;
+          font-variant-east-asian: full-width;
+      }
+  
+      .terminal-1319604136-title {
+          font-size: 18px;
+          font-weight: bold;
+          font-family: arial;
+      }
+  
+      .terminal-1319604136-r1 { fill: #1e1e1e }
+  .terminal-1319604136-r2 { fill: #0178d4 }
+  .terminal-1319604136-r3 { fill: #c5c8c6 }
+  .terminal-1319604136-r4 { fill: #e2e2e2 }
+  .terminal-1319604136-r5 { fill: #1e1e1e;font-style: italic; }
+  .terminal-1319604136-r6 { fill: #ff0000;font-style: italic; }
+  .terminal-1319604136-r7 { fill: #e1e1e1 }
+      </style>
+  
+      <defs>
+      <clipPath id="terminal-1319604136-clip-terminal">
+        <rect x="0" y="0" width="975.0" height="584.5999999999999" />
+      </clipPath>
+      <clipPath id="terminal-1319604136-line-0">
+      <rect x="0" y="1.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-1">
+      <rect x="0" y="25.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-2">
+      <rect x="0" y="50.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-3">
+      <rect x="0" y="74.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-4">
+      <rect x="0" y="99.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-5">
+      <rect x="0" y="123.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-6">
+      <rect x="0" y="147.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-7">
+      <rect x="0" y="172.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-8">
+      <rect x="0" y="196.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-9">
+      <rect x="0" y="221.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-10">
+      <rect x="0" y="245.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-11">
+      <rect x="0" y="269.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-12">
+      <rect x="0" y="294.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-13">
+      <rect x="0" y="318.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-14">
+      <rect x="0" y="343.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-15">
+      <rect x="0" y="367.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-16">
+      <rect x="0" y="391.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-17">
+      <rect x="0" y="416.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-18">
+      <rect x="0" y="440.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-19">
+      <rect x="0" y="465.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-20">
+      <rect x="0" y="489.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-line-21">
+      <rect x="0" y="513.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-1319604136-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-1319604136-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">FruitsApp</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-1319604136-clip-terminal)">
+      <rect fill="#0178d4" x="0" y="1.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="1.5" width="951.6" 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="#0178d4" x="0" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="25.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="25.9" width="61" height="24.65" shape-rendering="crispEdges"/><rect fill="#e1e1e1" x="97.6" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="109.8" y="25.9" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="158.6" y="25.9" width="780.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="939.4" y="25.9" width="24.4" 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="#0178d4" x="0" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="12.2" y="50.3" width="951.6" 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="976" 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-1319604136-matrix">
+      <text class="terminal-1319604136-r1" x="0" y="20" textLength="12.2" clip-path="url(#terminal-1319604136-line-0)">▊</text><text class="terminal-1319604136-r2" x="12.2" y="20" textLength="951.6" clip-path="url(#terminal-1319604136-line-0)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-1319604136-r2" x="963.8" y="20" textLength="12.2" clip-path="url(#terminal-1319604136-line-0)">▎</text><text class="terminal-1319604136-r3" x="976" y="20" textLength="12.2" clip-path="url(#terminal-1319604136-line-0)">
+  </text><text class="terminal-1319604136-r1" x="0" y="44.4" textLength="12.2" clip-path="url(#terminal-1319604136-line-1)">▊</text><text class="terminal-1319604136-r4" x="36.6" y="44.4" textLength="61" clip-path="url(#terminal-1319604136-line-1)">straw</text><text class="terminal-1319604136-r5" x="97.6" y="44.4" textLength="12.2" clip-path="url(#terminal-1319604136-line-1)">b</text><text class="terminal-1319604136-r6" x="109.8" y="44.4" textLength="48.8" clip-path="url(#terminal-1319604136-line-1)">erry</text><text class="terminal-1319604136-r2" x="963.8" y="44.4" textLength="12.2" clip-path="url(#terminal-1319604136-line-1)">▎</text><text class="terminal-1319604136-r3" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-1319604136-line-1)">
+  </text><text class="terminal-1319604136-r1" x="0" y="68.8" textLength="12.2" clip-path="url(#terminal-1319604136-line-2)">▊</text><text class="terminal-1319604136-r2" x="12.2" y="68.8" textLength="951.6" clip-path="url(#terminal-1319604136-line-2)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-1319604136-r2" x="963.8" y="68.8" textLength="12.2" clip-path="url(#terminal-1319604136-line-2)">▎</text><text class="terminal-1319604136-r3" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-1319604136-line-2)">
+  </text><text class="terminal-1319604136-r3" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-1319604136-line-3)">
+  </text><text class="terminal-1319604136-r3" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-1319604136-line-4)">
+  </text><text class="terminal-1319604136-r3" x="976" y="142" textLength="12.2" clip-path="url(#terminal-1319604136-line-5)">
+  </text><text class="terminal-1319604136-r3" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-1319604136-line-6)">
+  </text><text class="terminal-1319604136-r3" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-1319604136-line-7)">
+  </text><text class="terminal-1319604136-r3" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-1319604136-line-8)">
+  </text><text class="terminal-1319604136-r3" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-1319604136-line-9)">
+  </text><text class="terminal-1319604136-r3" x="976" y="264" textLength="12.2" clip-path="url(#terminal-1319604136-line-10)">
+  </text><text class="terminal-1319604136-r3" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-1319604136-line-11)">
+  </text><text class="terminal-1319604136-r3" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-1319604136-line-12)">
+  </text><text class="terminal-1319604136-r3" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-1319604136-line-13)">
+  </text><text class="terminal-1319604136-r3" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-1319604136-line-14)">
+  </text><text class="terminal-1319604136-r3" x="976" y="386" textLength="12.2" clip-path="url(#terminal-1319604136-line-15)">
+  </text><text class="terminal-1319604136-r3" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-1319604136-line-16)">
+  </text><text class="terminal-1319604136-r3" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-1319604136-line-17)">
+  </text><text class="terminal-1319604136-r3" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-1319604136-line-18)">
+  </text><text class="terminal-1319604136-r3" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-1319604136-line-19)">
+  </text><text class="terminal-1319604136-r3" x="976" y="508" textLength="12.2" clip-path="url(#terminal-1319604136-line-20)">
+  </text><text class="terminal-1319604136-r3" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-1319604136-line-21)">
+  </text><text class="terminal-1319604136-r3" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-1319604136-line-22)">
   </text>
       </g>
       </g>
@@ -16813,138 +16973,138 @@
           font-weight: 700;
       }
   
-      .terminal-3846064726-matrix {
+      .terminal-922438230-matrix {
           font-family: Fira Code, monospace;
           font-size: 20px;
           line-height: 24.4px;
           font-variant-east-asian: full-width;
       }
   
-      .terminal-3846064726-title {
+      .terminal-922438230-title {
           font-size: 18px;
           font-weight: bold;
           font-family: arial;
       }
   
-      .terminal-3846064726-r1 { fill: #e1e1e1 }
-  .terminal-3846064726-r2 { fill: #c5c8c6 }
-  .terminal-3846064726-r3 { fill: #1e1e1e }
-  .terminal-3846064726-r4 { fill: #7b3042 }
-  .terminal-3846064726-r5 { fill: #e2e2e2 }
-  .terminal-3846064726-r6 { fill: #3a7e4f }
-  .terminal-3846064726-r7 { fill: #b93c5b }
-  .terminal-3846064726-r8 { fill: #121212 }
-  .terminal-3846064726-r9 { fill: #787878 }
+      .terminal-922438230-r1 { fill: #e1e1e1 }
+  .terminal-922438230-r2 { fill: #c5c8c6 }
+  .terminal-922438230-r3 { fill: #1e1e1e }
+  .terminal-922438230-r4 { fill: #7b3042 }
+  .terminal-922438230-r5 { fill: #e2e2e2 }
+  .terminal-922438230-r6 { fill: #3a7e4f }
+  .terminal-922438230-r7 { fill: #b93c5b }
+  .terminal-922438230-r8 { fill: #121212 }
+  .terminal-922438230-r9 { fill: #787878 }
       </style>
   
       <defs>
-      <clipPath id="terminal-3846064726-clip-terminal">
+      <clipPath id="terminal-922438230-clip-terminal">
         <rect x="0" y="0" width="975.0" height="584.5999999999999" />
       </clipPath>
-      <clipPath id="terminal-3846064726-line-0">
+      <clipPath id="terminal-922438230-line-0">
       <rect x="0" y="1.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-1">
+  <clipPath id="terminal-922438230-line-1">
       <rect x="0" y="25.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-2">
+  <clipPath id="terminal-922438230-line-2">
       <rect x="0" y="50.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-3">
+  <clipPath id="terminal-922438230-line-3">
       <rect x="0" y="74.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-4">
+  <clipPath id="terminal-922438230-line-4">
       <rect x="0" y="99.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-5">
+  <clipPath id="terminal-922438230-line-5">
       <rect x="0" y="123.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-6">
+  <clipPath id="terminal-922438230-line-6">
       <rect x="0" y="147.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-7">
+  <clipPath id="terminal-922438230-line-7">
       <rect x="0" y="172.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-8">
+  <clipPath id="terminal-922438230-line-8">
       <rect x="0" y="196.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-9">
+  <clipPath id="terminal-922438230-line-9">
       <rect x="0" y="221.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-10">
+  <clipPath id="terminal-922438230-line-10">
       <rect x="0" y="245.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-11">
+  <clipPath id="terminal-922438230-line-11">
       <rect x="0" y="269.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-12">
+  <clipPath id="terminal-922438230-line-12">
       <rect x="0" y="294.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-13">
+  <clipPath id="terminal-922438230-line-13">
       <rect x="0" y="318.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-14">
+  <clipPath id="terminal-922438230-line-14">
       <rect x="0" y="343.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-15">
+  <clipPath id="terminal-922438230-line-15">
       <rect x="0" y="367.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-16">
+  <clipPath id="terminal-922438230-line-16">
       <rect x="0" y="391.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-17">
+  <clipPath id="terminal-922438230-line-17">
       <rect x="0" y="416.3" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-18">
+  <clipPath id="terminal-922438230-line-18">
       <rect x="0" y="440.7" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-19">
+  <clipPath id="terminal-922438230-line-19">
       <rect x="0" y="465.1" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-20">
+  <clipPath id="terminal-922438230-line-20">
       <rect x="0" y="489.5" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-21">
+  <clipPath id="terminal-922438230-line-21">
       <rect x="0" y="513.9" width="976" height="24.65"/>
               </clipPath>
-  <clipPath id="terminal-3846064726-line-22">
+  <clipPath id="terminal-922438230-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-3846064726-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">InputApp</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-922438230-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">InputApp</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-3846064726-clip-terminal)">
-      <rect fill="#1e1e1e" x="0" y="1.5" width="976" 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="#7b3042" x="24.4" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="25.9" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="25.9" width="24.4" 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="#7b3042" x="24.4" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="50.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="61" y="50.3" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="97.6" y="50.3" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="915" y="50.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="50.3" width="24.4" 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="#7b3042" x="24.4" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="74.7" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="74.7" width="24.4" 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="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#3a7e4f" x="24.4" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="123.5" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="123.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#3a7e4f" x="24.4" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="147.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="61" y="147.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="85.4" y="147.9" width="829.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="915" y="147.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="147.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#3a7e4f" x="24.4" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="172.3" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="172.3" width="24.4" 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="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#b93c5b" x="24.4" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="221.1" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="221.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#b93c5b" x="24.4" y="245.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="61" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#e1e1e1" x="85.4" y="245.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="97.6" y="245.5" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="915" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="245.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#b93c5b" x="24.4" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="269.9" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="269.9" width="24.4" 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="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="318.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="318.7" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="318.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="318.7" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="343.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="343.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="343.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="61" y="343.1" width="366" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="427" y="343.1" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="915" y="343.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="343.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="343.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="367.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="367.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="367.5" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="367.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="367.5" width="24.4" 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-3846064726-matrix">
-      <text class="terminal-3846064726-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-3846064726-line-0)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="44.4" textLength="12.2" clip-path="url(#terminal-3846064726-line-1)">▊</text><text class="terminal-3846064726-r4" x="36.6" y="44.4" textLength="902.8" clip-path="url(#terminal-3846064726-line-1)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-3846064726-r4" x="939.4" y="44.4" textLength="12.2" clip-path="url(#terminal-3846064726-line-1)">▎</text><text class="terminal-3846064726-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-3846064726-line-1)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="68.8" textLength="12.2" clip-path="url(#terminal-3846064726-line-2)">▊</text><text class="terminal-3846064726-r5" x="61" y="68.8" textLength="36.6" clip-path="url(#terminal-3846064726-line-2)">-2&#160;</text><text class="terminal-3846064726-r4" x="939.4" y="68.8" textLength="12.2" clip-path="url(#terminal-3846064726-line-2)">▎</text><text class="terminal-3846064726-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-3846064726-line-2)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="93.2" textLength="12.2" clip-path="url(#terminal-3846064726-line-3)">▊</text><text class="terminal-3846064726-r4" x="36.6" y="93.2" textLength="902.8" clip-path="url(#terminal-3846064726-line-3)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-3846064726-r4" x="939.4" y="93.2" textLength="12.2" clip-path="url(#terminal-3846064726-line-3)">▎</text><text class="terminal-3846064726-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-3846064726-line-3)">
-  </text><text class="terminal-3846064726-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-3846064726-line-4)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="142" textLength="12.2" clip-path="url(#terminal-3846064726-line-5)">▊</text><text class="terminal-3846064726-r6" x="36.6" y="142" textLength="902.8" clip-path="url(#terminal-3846064726-line-5)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-3846064726-r6" x="939.4" y="142" textLength="12.2" clip-path="url(#terminal-3846064726-line-5)">▎</text><text class="terminal-3846064726-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-3846064726-line-5)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="166.4" textLength="12.2" clip-path="url(#terminal-3846064726-line-6)">▊</text><text class="terminal-3846064726-r5" x="61" y="166.4" textLength="24.4" clip-path="url(#terminal-3846064726-line-6)">3&#160;</text><text class="terminal-3846064726-r6" x="939.4" y="166.4" textLength="12.2" clip-path="url(#terminal-3846064726-line-6)">▎</text><text class="terminal-3846064726-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-3846064726-line-6)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="190.8" textLength="12.2" clip-path="url(#terminal-3846064726-line-7)">▊</text><text class="terminal-3846064726-r6" x="36.6" y="190.8" textLength="902.8" clip-path="url(#terminal-3846064726-line-7)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-3846064726-r6" x="939.4" y="190.8" textLength="12.2" clip-path="url(#terminal-3846064726-line-7)">▎</text><text class="terminal-3846064726-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-3846064726-line-7)">
-  </text><text class="terminal-3846064726-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-3846064726-line-8)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="239.6" textLength="12.2" clip-path="url(#terminal-3846064726-line-9)">▊</text><text class="terminal-3846064726-r7" x="36.6" y="239.6" textLength="902.8" clip-path="url(#terminal-3846064726-line-9)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-3846064726-r7" x="939.4" y="239.6" textLength="12.2" clip-path="url(#terminal-3846064726-line-9)">▎</text><text class="terminal-3846064726-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-3846064726-line-9)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="264" textLength="12.2" clip-path="url(#terminal-3846064726-line-10)">▊</text><text class="terminal-3846064726-r5" x="61" y="264" textLength="24.4" clip-path="url(#terminal-3846064726-line-10)">-2</text><text class="terminal-3846064726-r7" x="939.4" y="264" textLength="12.2" clip-path="url(#terminal-3846064726-line-10)">▎</text><text class="terminal-3846064726-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-3846064726-line-10)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="288.4" textLength="12.2" clip-path="url(#terminal-3846064726-line-11)">▊</text><text class="terminal-3846064726-r7" x="36.6" y="288.4" textLength="902.8" clip-path="url(#terminal-3846064726-line-11)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-3846064726-r7" x="939.4" y="288.4" textLength="12.2" clip-path="url(#terminal-3846064726-line-11)">▎</text><text class="terminal-3846064726-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-3846064726-line-11)">
-  </text><text class="terminal-3846064726-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-3846064726-line-12)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="337.2" textLength="12.2" clip-path="url(#terminal-3846064726-line-13)">▊</text><text class="terminal-3846064726-r8" x="36.6" y="337.2" textLength="902.8" clip-path="url(#terminal-3846064726-line-13)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-3846064726-r8" x="939.4" y="337.2" textLength="12.2" clip-path="url(#terminal-3846064726-line-13)">▎</text><text class="terminal-3846064726-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-3846064726-line-13)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="361.6" textLength="12.2" clip-path="url(#terminal-3846064726-line-14)">▊</text><text class="terminal-3846064726-r9" x="61" y="361.6" textLength="366" clip-path="url(#terminal-3846064726-line-14)">Enter&#160;a&#160;number&#160;between&#160;1&#160;and&#160;5</text><text class="terminal-3846064726-r8" x="939.4" y="361.6" textLength="12.2" clip-path="url(#terminal-3846064726-line-14)">▎</text><text class="terminal-3846064726-r2" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-3846064726-line-14)">
-  </text><text class="terminal-3846064726-r3" x="24.4" y="386" textLength="12.2" clip-path="url(#terminal-3846064726-line-15)">▊</text><text class="terminal-3846064726-r8" x="36.6" y="386" textLength="902.8" clip-path="url(#terminal-3846064726-line-15)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-3846064726-r8" x="939.4" y="386" textLength="12.2" clip-path="url(#terminal-3846064726-line-15)">▎</text><text class="terminal-3846064726-r2" x="976" y="386" textLength="12.2" clip-path="url(#terminal-3846064726-line-15)">
-  </text><text class="terminal-3846064726-r2" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-3846064726-line-16)">
-  </text><text class="terminal-3846064726-r2" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-3846064726-line-17)">
-  </text><text class="terminal-3846064726-r2" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-3846064726-line-18)">
-  </text><text class="terminal-3846064726-r2" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-3846064726-line-19)">
-  </text><text class="terminal-3846064726-r2" x="976" y="508" textLength="12.2" clip-path="url(#terminal-3846064726-line-20)">
-  </text><text class="terminal-3846064726-r2" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-3846064726-line-21)">
-  </text><text class="terminal-3846064726-r2" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-3846064726-line-22)">
+      <g transform="translate(9, 41)" clip-path="url(#terminal-922438230-clip-terminal)">
+      <rect fill="#1e1e1e" x="0" y="1.5" width="976" 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="#7b3042" x="24.4" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="25.9" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="25.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="25.9" width="24.4" 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="#7b3042" x="24.4" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="50.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="61" y="50.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="85.4" y="50.3" width="829.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="915" y="50.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="50.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="50.3" width="24.4" 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="#7b3042" x="24.4" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="74.7" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="74.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="74.7" width="24.4" 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="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#3a7e4f" x="24.4" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="123.5" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="123.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="123.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#3a7e4f" x="24.4" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="147.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="61" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="73.2" y="147.9" width="841.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="915" y="147.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="147.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="147.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#3a7e4f" x="24.4" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="172.3" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="172.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="172.3" width="24.4" 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="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#b93c5b" x="24.4" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="221.1" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="221.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="221.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#b93c5b" x="24.4" y="245.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="61" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#e1e1e1" x="85.4" y="245.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="97.6" y="245.5" width="817.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="915" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="245.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="245.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#b93c5b" x="24.4" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="269.9" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="269.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="269.9" width="24.4" 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="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="318.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="318.7" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="318.7" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="318.7" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="343.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="343.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="343.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="61" y="343.1" width="366" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="427" y="343.1" width="488" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="915" y="343.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="343.1" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="343.1" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="367.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="24.4" y="367.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#262626" x="36.6" y="367.5" width="902.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="939.4" y="367.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="367.5" width="24.4" 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-922438230-matrix">
+      <text class="terminal-922438230-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-922438230-line-0)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="44.4" textLength="12.2" clip-path="url(#terminal-922438230-line-1)">▊</text><text class="terminal-922438230-r4" x="36.6" y="44.4" textLength="902.8" clip-path="url(#terminal-922438230-line-1)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-922438230-r4" x="939.4" y="44.4" textLength="12.2" clip-path="url(#terminal-922438230-line-1)">▎</text><text class="terminal-922438230-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-922438230-line-1)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="68.8" textLength="12.2" clip-path="url(#terminal-922438230-line-2)">▊</text><text class="terminal-922438230-r5" x="61" y="68.8" textLength="24.4" clip-path="url(#terminal-922438230-line-2)">-2</text><text class="terminal-922438230-r4" x="939.4" y="68.8" textLength="12.2" clip-path="url(#terminal-922438230-line-2)">▎</text><text class="terminal-922438230-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-922438230-line-2)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="93.2" textLength="12.2" clip-path="url(#terminal-922438230-line-3)">▊</text><text class="terminal-922438230-r4" x="36.6" y="93.2" textLength="902.8" clip-path="url(#terminal-922438230-line-3)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-922438230-r4" x="939.4" y="93.2" textLength="12.2" clip-path="url(#terminal-922438230-line-3)">▎</text><text class="terminal-922438230-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-922438230-line-3)">
+  </text><text class="terminal-922438230-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-922438230-line-4)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="142" textLength="12.2" clip-path="url(#terminal-922438230-line-5)">▊</text><text class="terminal-922438230-r6" x="36.6" y="142" textLength="902.8" clip-path="url(#terminal-922438230-line-5)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-922438230-r6" x="939.4" y="142" textLength="12.2" clip-path="url(#terminal-922438230-line-5)">▎</text><text class="terminal-922438230-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-922438230-line-5)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="166.4" textLength="12.2" clip-path="url(#terminal-922438230-line-6)">▊</text><text class="terminal-922438230-r5" x="61" y="166.4" textLength="12.2" clip-path="url(#terminal-922438230-line-6)">3</text><text class="terminal-922438230-r6" x="939.4" y="166.4" textLength="12.2" clip-path="url(#terminal-922438230-line-6)">▎</text><text class="terminal-922438230-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-922438230-line-6)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="190.8" textLength="12.2" clip-path="url(#terminal-922438230-line-7)">▊</text><text class="terminal-922438230-r6" x="36.6" y="190.8" textLength="902.8" clip-path="url(#terminal-922438230-line-7)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-922438230-r6" x="939.4" y="190.8" textLength="12.2" clip-path="url(#terminal-922438230-line-7)">▎</text><text class="terminal-922438230-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-922438230-line-7)">
+  </text><text class="terminal-922438230-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-922438230-line-8)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="239.6" textLength="12.2" clip-path="url(#terminal-922438230-line-9)">▊</text><text class="terminal-922438230-r7" x="36.6" y="239.6" textLength="902.8" clip-path="url(#terminal-922438230-line-9)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-922438230-r7" x="939.4" y="239.6" textLength="12.2" clip-path="url(#terminal-922438230-line-9)">▎</text><text class="terminal-922438230-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-922438230-line-9)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="264" textLength="12.2" clip-path="url(#terminal-922438230-line-10)">▊</text><text class="terminal-922438230-r5" x="61" y="264" textLength="24.4" clip-path="url(#terminal-922438230-line-10)">-2</text><text class="terminal-922438230-r7" x="939.4" y="264" textLength="12.2" clip-path="url(#terminal-922438230-line-10)">▎</text><text class="terminal-922438230-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-922438230-line-10)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="288.4" textLength="12.2" clip-path="url(#terminal-922438230-line-11)">▊</text><text class="terminal-922438230-r7" x="36.6" y="288.4" textLength="902.8" clip-path="url(#terminal-922438230-line-11)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-922438230-r7" x="939.4" y="288.4" textLength="12.2" clip-path="url(#terminal-922438230-line-11)">▎</text><text class="terminal-922438230-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-922438230-line-11)">
+  </text><text class="terminal-922438230-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-922438230-line-12)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="337.2" textLength="12.2" clip-path="url(#terminal-922438230-line-13)">▊</text><text class="terminal-922438230-r8" x="36.6" y="337.2" textLength="902.8" clip-path="url(#terminal-922438230-line-13)">▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔</text><text class="terminal-922438230-r8" x="939.4" y="337.2" textLength="12.2" clip-path="url(#terminal-922438230-line-13)">▎</text><text class="terminal-922438230-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-922438230-line-13)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="361.6" textLength="12.2" clip-path="url(#terminal-922438230-line-14)">▊</text><text class="terminal-922438230-r9" x="61" y="361.6" textLength="366" clip-path="url(#terminal-922438230-line-14)">Enter&#160;a&#160;number&#160;between&#160;1&#160;and&#160;5</text><text class="terminal-922438230-r8" x="939.4" y="361.6" textLength="12.2" clip-path="url(#terminal-922438230-line-14)">▎</text><text class="terminal-922438230-r2" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-922438230-line-14)">
+  </text><text class="terminal-922438230-r3" x="24.4" y="386" textLength="12.2" clip-path="url(#terminal-922438230-line-15)">▊</text><text class="terminal-922438230-r8" x="36.6" y="386" textLength="902.8" clip-path="url(#terminal-922438230-line-15)">▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁</text><text class="terminal-922438230-r8" x="939.4" y="386" textLength="12.2" clip-path="url(#terminal-922438230-line-15)">▎</text><text class="terminal-922438230-r2" x="976" y="386" textLength="12.2" clip-path="url(#terminal-922438230-line-15)">
+  </text><text class="terminal-922438230-r2" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-922438230-line-16)">
+  </text><text class="terminal-922438230-r2" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-922438230-line-17)">
+  </text><text class="terminal-922438230-r2" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-922438230-line-18)">
+  </text><text class="terminal-922438230-r2" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-922438230-line-19)">
+  </text><text class="terminal-922438230-r2" x="976" y="508" textLength="12.2" clip-path="url(#terminal-922438230-line-20)">
+  </text><text class="terminal-922438230-r2" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-922438230-line-21)">
+  </text><text class="terminal-922438230-r2" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-922438230-line-22)">
   </text>
       </g>
       </g>
diff --git a/tests/snapshot_tests/conftest.py b/tests/snapshot_tests/conftest.py
--- a/tests/snapshot_tests/conftest.py
+++ b/tests/snapshot_tests/conftest.py
@@ -40,7 +40,7 @@ def snap_compare(
 
     def compare(
         app_path: str | PurePath,
-        press: Iterable[str] = ("_",),
+        press: Iterable[str] = (),
         terminal_size: tuple[int, int] = (80, 24),
         run_before: Callable[[Pilot], Awaitable[None] | None] | None = None,
     ) -> bool:
diff --git a/tests/snapshot_tests/snapshot_apps/input_suggestions.py b/tests/snapshot_tests/snapshot_apps/input_suggestions.py
new file mode 100644
--- /dev/null
+++ b/tests/snapshot_tests/snapshot_apps/input_suggestions.py
@@ -0,0 +1,23 @@
+from textual.app import App, ComposeResult
+from textual.suggester import SuggestFromList
+from textual.widgets import Input
+
+
+fruits = ["apple", "pear", "mango", "peach", "strawberry", "blueberry", "banana"]
+
+
+class FruitsApp(App[None]):
+    CSS = """
+    Input > .input--suggestion {
+        color: red;
+        text-style: italic;
+    }
+    """
+
+    def compose(self) -> ComposeResult:
+        yield Input("straw", suggester=SuggestFromList(fruits))
+
+
+if __name__ == "__main__":
+    app = FruitsApp()
+    app.run()
diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py
--- a/tests/snapshot_tests/test_snapshots.py
+++ b/tests/snapshot_tests/test_snapshots.py
@@ -97,6 +97,10 @@ def test_input_validation(snap_compare):
     assert snap_compare(SNAPSHOT_APPS_DIR / "input_validation.py", press=press)
 
 
+def test_input_suggestions(snap_compare):
+    assert snap_compare(SNAPSHOT_APPS_DIR / "input_suggestions.py", press=[])
+
+
 def test_buttons_render(snap_compare):
     # Testing button rendering. We press tab to focus the first button too.
     assert snap_compare(WIDGET_EXAMPLES_DIR / "button.py", press=["tab"])
@@ -245,15 +249,19 @@ def test_progress_bar_completed_styled(snap_compare):
 def test_select(snap_compare):
     assert snap_compare(WIDGET_EXAMPLES_DIR / "select_widget.py")
 
+
 def test_selection_list_selected(snap_compare):
     assert snap_compare(WIDGET_EXAMPLES_DIR / "selection_list_selected.py")
 
+
 def test_selection_list_selections(snap_compare):
     assert snap_compare(WIDGET_EXAMPLES_DIR / "selection_list_selections.py")
 
+
 def test_selection_list_tuples(snap_compare):
     assert snap_compare(WIDGET_EXAMPLES_DIR / "selection_list_tuples.py")
 
+
 def test_select_expanded(snap_compare):
     assert snap_compare(
         WIDGET_EXAMPLES_DIR / "select_widget.py", press=["tab", "enter"]
@@ -482,13 +490,17 @@ def test_dock_scroll2(snap_compare):
 def test_dock_scroll_off_by_one(snap_compare):
     # https://github.com/Textualize/textual/issues/2525
     assert snap_compare(
-        SNAPSHOT_APPS_DIR / "dock_scroll_off_by_one.py", terminal_size=(80, 25)
+        SNAPSHOT_APPS_DIR / "dock_scroll_off_by_one.py",
+        terminal_size=(80, 25),
+        press=["_"],
     )
 
 
 def test_scroll_to(snap_compare):
     # https://github.com/Textualize/textual/issues/2525
-    assert snap_compare(SNAPSHOT_APPS_DIR / "scroll_to.py", terminal_size=(80, 25))
+    assert snap_compare(
+        SNAPSHOT_APPS_DIR / "scroll_to.py", terminal_size=(80, 25), press=["_"]
+    )
 
 
 def test_auto_fr(snap_compare):
diff --git a/tests/suggester/test_input_suggestions.py b/tests/suggester/test_input_suggestions.py
new file mode 100644
--- /dev/null
+++ b/tests/suggester/test_input_suggestions.py
@@ -0,0 +1,101 @@
+import string
+
+import pytest
+
+from textual.app import App, ComposeResult
+from textual.suggester import SuggestFromList
+from textual.widgets import Input
+
+
+class SuggestionsApp(App[ComposeResult]):
+    def __init__(self, suggestions):
+        self.suggestions = suggestions
+        self.input = Input(suggester=SuggestFromList(self.suggestions))
+        super().__init__()
+
+    def compose(self) -> ComposeResult:
+        yield self.input
+
+
+async def test_no_suggestions():
+    app = SuggestionsApp([])
+    async with app.run_test() as pilot:
+        assert app.input._suggestion == ""
+        await pilot.press("a")
+        assert app.input._suggestion == ""
+
+
+async def test_suggestion():
+    app = SuggestionsApp(["hello"])
+    async with app.run_test() as pilot:
+        for char in "hello":
+            await pilot.press(char)
+            assert app.input._suggestion == "hello"
+
+
+async def test_accept_suggestion():
+    app = SuggestionsApp(["hello"])
+    async with app.run_test() as pilot:
+        await pilot.press("h")
+        await pilot.press("right")
+        assert app.input.value == "hello"
+
+
+async def test_no_suggestion_on_empty_value():
+    app = SuggestionsApp(["hello"])
+    async with app.run_test():
+        assert app.input._suggestion == ""
+
+
+async def test_no_suggestion_on_empty_value_after_deleting():
+    app = SuggestionsApp(["hello"])
+    async with app.run_test() as pilot:
+        await pilot.press("h", "e", "backspace", "backspace")
+        assert app.input.value == ""  # Sanity check.
+        assert app.input._suggestion == ""
+
+
+async def test_suggestion_shows_up_after_deleting_extra_chars():
+    app = SuggestionsApp(["hello"])
+    async with app.run_test() as pilot:
+        await pilot.press(*"help")
+        assert app.input._suggestion == ""
+        await pilot.press("backspace")
+        assert app.input._suggestion == "hello"
+
+
+async def test_suggestion_shows_up_after_deleting_extra_chars_in_middle_of_word():
+    app = SuggestionsApp(["hello"])
+    async with app.run_test() as pilot:
+        await pilot.press(*"hefl")
+        assert app.input._suggestion == ""
+        await pilot.press("left", "backspace")
+        assert app.input._suggestion == "hello"
+
+
+@pytest.mark.parametrize(
+    ("suggestion", "truncate_at"),
+    [
+        (".......", 3),
+        ("hey there", 3),
+        ("Olá, tudo bem?", 3),
+        ("áàóãõñç", 2),
+        (string.punctuation, 3),
+        (string.punctuation[::-1], 5),
+        (string.punctuation[::3], 5),
+    ],
+)
+async def test_suggestion_with_special_characters(suggestion: str, truncate_at: int):
+    app = SuggestionsApp([suggestion])
+    async with app.run_test() as pilot:
+        await pilot.press(*suggestion[:truncate_at])
+        assert app.input._suggestion == suggestion
+
+
+async def test_suggestion_priority():
+    app = SuggestionsApp(["dog", "dad"])
+    async with app.run_test() as pilot:
+        await pilot.press("d")
+        assert app.input._suggestion == "dog"
+        await pilot.press("a")
+        assert app.input._suggestion == "dad"
diff --git a/tests/suggester/test_suggest_from_list.py b/tests/suggester/test_suggest_from_list.py
new file mode 100644
--- /dev/null
+++ b/tests/suggester/test_suggest_from_list.py
@@ -0,0 +1,55 @@
+from __future__ import annotations
+
+import pytest
+
+from textual.dom import DOMNode
+from textual.suggester import SuggestFromList, SuggestionReady
+
+countries = ["England", "Portugal", "Scotland", "portugal", "PORTUGAL"]
+
+
+class LogListNode(DOMNode):
+    def __init__(self, log_list: list[tuple[str, str]]) -> None:
+        self.log_list = log_list
+
+    def post_message(self, message: SuggestionReady):
+        # We hijack post_message so we can intercept messages without creating a full app.
+        self.log_list.append((message.suggestion, message.value))
+
+
+async def test_first_suggestion_has_priority():
+    suggester = SuggestFromList(countries)
+
+    assert "Portugal" == await suggester.get_suggestion("P")
+
+
+@pytest.mark.parametrize("value", ["s", "S", "sc", "sC", "Sc", "SC"])
+async def test_case_insensitive_suggestions(value):
+    suggester = SuggestFromList(countries, case_sensitive=False)
+    log = []
+
+    await suggester._get_suggestion(LogListNode(log), value)
+    assert log == [("Scotland", value)]
+
+
+@pytest.mark.parametrize(
+    "value",
+    [
+        "p",
+        "P",
+        "po",
+        "Po",
+        "pO",
+        "PO",
+        "port",
+        "Port",
+        "pORT",
+        "PORT",
+    ],
+)
+async def test_first_suggestion_has_priority_case_insensitive(value):
+    suggester = SuggestFromList(countries, case_sensitive=False)
+    log = []
+
+    await suggester._get_suggestion(LogListNode(log), value)
+    assert log == [("Portugal", value)]
diff --git a/tests/suggester/test_suggester.py b/tests/suggester/test_suggester.py
new file mode 100644
--- /dev/null
+++ b/tests/suggester/test_suggester.py
@@ -0,0 +1,111 @@
+from __future__ import annotations
+
+import pytest
+
+from textual.dom import DOMNode
+from textual.suggester import Suggester, SuggestionReady
+
+
+class FillSuggester(Suggester):
+    async def get_suggestion(self, value: str):
+        if len(value) <= 10:
+            return f"{value:x<10}"
+
+
+class LogListNode(DOMNode):
+    def __init__(self, log_list: list[tuple[str, str]]) -> None:
+        self.log_list = log_list
+
+    def post_message(self, message: SuggestionReady):
+        # We hijack post_message so we can intercept messages without creating a full app.
+        self.log_list.append((message.suggestion, message.value))
+
+
+async def test_cache_on():
+    log = []
+
+    class MySuggester(Suggester):
+        async def get_suggestion(self, value: str):
+            log.append(value)
+            return value
+
+    suggester = MySuggester(use_cache=True)
+    await suggester._get_suggestion(DOMNode(), "hello")
+    assert log == ["hello"]
+    await suggester._get_suggestion(DOMNode(), "hello")
+    assert log == ["hello"]
+
+
+async def test_cache_off():
+    log = []
+
+    class MySuggester(Suggester):
+        async def get_suggestion(self, value: str):
+            log.append(value)
+            return value
+
+    suggester = MySuggester(use_cache=False)
+    await suggester._get_suggestion(DOMNode(), "hello")
+    assert log == ["hello"]
+    await suggester._get_suggestion(DOMNode(), "hello")
+    assert log == ["hello", "hello"]
+
+
+async def test_suggestion_ready_message():
+    log = []
+    suggester = FillSuggester()
+    await suggester._get_suggestion(LogListNode(log), "hello")
+    assert log == [("helloxxxxx", "hello")]
+    await suggester._get_suggestion(LogListNode(log), "world")
+    assert log == [("helloxxxxx", "hello"), ("worldxxxxx", "world")]
+
+
+async def test_no_message_if_no_suggestion():
+    log = []
+    suggester = FillSuggester()
+    await suggester._get_suggestion(LogListNode(log), "this is a longer string")
+    assert log == []
+
+
+async def test_suggestion_ready_message_on_cache_hit():
+    log = []
+    suggester = FillSuggester(use_cache=True)
+    await suggester._get_suggestion(LogListNode(log), "hello")
+    assert log == [("helloxxxxx", "hello")]
+    await suggester._get_suggestion(LogListNode(log), "hello")
+    assert log == [("helloxxxxx", "hello"), ("helloxxxxx", "hello")]
+
+
+@pytest.mark.parametrize(
+    "value",
+    [
+        "hello",
+        "HELLO",
+        "HeLlO",
+        "Hello",
+        "hELLO",
+    ],
+)
+async def test_case_insensitive_suggestions(value):
+    class MySuggester(Suggester):
+        async def get_suggestion(self, value: str):
+            assert "hello" == value
+
+    suggester = MySuggester(use_cache=False, case_sensitive=False)
+    await suggester._get_suggestion(DOMNode(), value)
+
+
+async def test_case_insensitive_cache_hits():
+    count = 0
+
+    class MySuggester(Suggester):
+        async def get_suggestion(self, value: str):
+            nonlocal count
+            count += 1
+            return value + "abc"
+
+    suggester = MySuggester(use_cache=True, case_sensitive=False)
+    hellos = ["hello", "HELLO", "HeLlO", "Hello", "hELLO"]
+    for hello in hellos:
+        await suggester._get_suggestion(DOMNode(), hello)
+    assert count == 1