Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-textual for openSUSE:Factory 
checked in at 2026-07-06 12:32:50
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-textual (Old)
 and      /work/SRC/openSUSE:Factory/.python-textual.new.1982 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-textual"

Mon Jul  6 12:32:50 2026 rev:12 rq:1363872 version:8.2.8

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-textual/python-textual.changes    
2026-05-20 15:26:00.013795864 +0200
+++ /work/SRC/openSUSE:Factory/.python-textual.new.1982/python-textual.changes  
2026-07-06 12:35:07.613236989 +0200
@@ -1,0 +2,15 @@
+Sun Jul  5 10:44:42 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 8.2.8:
+  * Fixes for extended key processing, and a crash bug for
+    clicking the screen padding area.
+  * Fixed parsing Kitty extended keys with multiple codepoints
+    https://github.com/Textualize/textual/pull/6592
+  * Fixed crash when clicking in the Screen's padding
+    https://github.com/Textualize/textual/pull/6598
+  * super+backspace is now an alias for ctrl+u in Input and
+    TextArea https://github.com/Textualize/textual/pull/6594
+  * alt+backspace (option+backspace on Mac) has the same effect
+    as ctrl+backspace in Input and TextArea
+
+-------------------------------------------------------------------

Old:
----
  textual-8.2.7.tar.gz

New:
----
  textual-8.2.8.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-textual.spec ++++++
--- /var/tmp/diff_new_pack.ARnNEA/_old  2026-07-06 12:35:09.049286886 +0200
+++ /var/tmp/diff_new_pack.ARnNEA/_new  2026-07-06 12:35:09.053287026 +0200
@@ -26,7 +26,7 @@
 %endif
 %{?sle15_python_module_pythons}
 Name:           python-textual%{psuffix}
-Version:        8.2.7
+Version:        8.2.8
 Release:        0
 Summary:        TUI framework for Python
 License:        MIT

++++++ textual-8.2.7.tar.gz -> textual-8.2.8.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/textual-8.2.7/PKG-INFO new/textual-8.2.8/PKG-INFO
--- old/textual-8.2.7/PKG-INFO  1970-01-01 01:00:00.000000000 +0100
+++ new/textual-8.2.8/PKG-INFO  1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: textual
-Version: 8.2.7
+Version: 8.2.8
 Summary: Modern Text User Interface framework
 License: MIT
 License-File: LICENSE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/textual-8.2.7/pyproject.toml 
new/textual-8.2.8/pyproject.toml
--- old/textual-8.2.7/pyproject.toml    1970-01-01 01:00:00.000000000 +0100
+++ new/textual-8.2.8/pyproject.toml    1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "textual"
-version = "8.2.7"
+version = "8.2.8"
 homepage = "https://github.com/Textualize/textual";
 repository = "https://github.com/Textualize/textual";
 documentation = "https://textual.textualize.io/";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/textual-8.2.7/src/textual/_compositor.py 
new/textual-8.2.8/src/textual/_compositor.py
--- old/textual-8.2.7/src/textual/_compositor.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/textual-8.2.8/src/textual/_compositor.py        1970-01-01 
01:00:00.000000000 +0100
@@ -923,11 +923,8 @@
         x -= region.x + gutter_left
         y -= region.y + gutter_right
 
-        if y < 0:
-            return None, None
-
-        if x < 0:
-            return widget, Offset(0, y)
+        if x < 0 or y < 0:
+            return widget, Offset(max(0, x), max(0, y))
 
         visible_screen_stack.set(widget.app._background_screens)
         line = widget.render_line(y)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/textual-8.2.7/src/textual/_xterm_parser.py 
new/textual-8.2.8/src/textual/_xterm_parser.py
--- old/textual-8.2.7/src/textual/_xterm_parser.py      1970-01-01 
01:00:00.000000000 +0100
+++ new/textual-8.2.8/src/textual/_xterm_parser.py      1970-01-01 
01:00:00.000000000 +0100
@@ -39,7 +39,7 @@
 """Set of special sequences."""
 
 _re_extended_key: Final[re.Pattern[str]] = re.compile(
-    r"\x1b\[((?:\d*;?){2,3})([u~ABCDEFHPQRS])"
+    r"\x1b\[((?:[\d:]*;?){2,3})([u~ABCDEFHPQRS])"
 )
 _re_in_band_window_resize: Final[re.Pattern[str]] = re.compile(
     
r"\x1b\[48;(\d+(?:\:.*?)?);(\d+(?:\:.*?)?);(\d+(?:\:.*?)?);(\d+(?:\:.*?)?)t"
@@ -334,8 +334,26 @@
             self._debug_log_file.close()
             self._debug_log_file = None
 
+    @classmethod
+    def _parse_colon_codepoints(cls, text_str: str) -> list[str | None]:
+        """Convert codepoints split on colons in to a list of characters.
+
+        Args:
+            text_str: String with groups of digits, separated by one or more 
colons.
+
+        Returns:
+            A list of characters.
+        """
+        if not text_str:
+            return [None]
+        characters: list[str | None] = [
+            chr(int(part)) if part.isdecimal() else chr(1)
+            for part in text_str.split(":")
+        ]
+        return characters
+
     @lru_cache(maxsize=1024)
-    def _parse_extended_key(self, sequence: str) -> events.Key | None:
+    def _parse_extended_key(self, sequence: str) -> list[events.Key] | None:
         """Parse a Kitty sequence.
 
         Args:
@@ -348,38 +366,47 @@
         if (match := _re_extended_key.fullmatch(sequence)) is None:
             return None
 
+        key_events: list[events.Key] = []
+
         codes, end = match.groups(default="")
         codepoint_str, modifiers_str, text_str, *_ = codes.split(";") + ["", 
"", ""]
-
         codepoint = int(codepoint_str or "1")
         modifiers = int(modifiers_str or "0")
-        text = chr(int(text_str)) if text_str else None
 
-        if not (key := FUNCTIONAL_KEYS.get(f"{codepoint}{end}", "")):
-            key = _character_to_key(text if text else chr(codepoint))
+        for text in self._parse_colon_codepoints(text_str):
+            if not (key := FUNCTIONAL_KEYS.get(f"{codepoint}{end}", "")):
+                key = _character_to_key(text if text else chr(codepoint))
 
-        key_tokens: list[str] = []
-        # The modifier is redundant on a modifier key
-        if modifiers and key not in MODIFIER_FUNCTIONAL_KEYS and text_str is 
not None:
-            modifier_bits = int(modifiers) - 1
-            # Not convinced of the utility in reporting caps_lock and num_lock
-            MODIFIERS = ("alt", "ctrl", "super", "hyper", "meta")
-            # Ignore caps_lock and num_lock modifiers
-            if modifier_bits & 1 and (text is None or text.isspace()):
-                key_tokens.append("shift")
-            for bit, modifier in enumerate(MODIFIERS, 1):
-                if modifier == "alt" and text is not None:
-                    continue
-                if modifier_bits & (1 << bit):
-                    key_tokens.append(modifier)
+            key_tokens: list[str] = []
+            # The modifier is redundant on a modifier key
+            if (
+                modifiers
+                and key not in MODIFIER_FUNCTIONAL_KEYS
+                and text_str is not None
+            ):
+                modifier_bits = int(modifiers) - 1
+                # Not convinced of the utility in reporting caps_lock and 
num_lock
+                MODIFIERS = ("alt", "ctrl", "super", "hyper", "meta")
+                # Ignore caps_lock and num_lock modifiers
+                if modifier_bits & 1 and (text is None or text.isspace()):
+                    key_tokens.append("shift")
+                for bit, modifier in enumerate(MODIFIERS, 1):
+                    if modifier == "alt" and text is not None:
+                        continue
+                    if modifier_bits & (1 << bit):
+                        key_tokens.append(modifier)
 
-        key_tokens.sort()
-        if key is not None:
-            key_tokens.append(key)
-        return events.Key(
-            "+".join(key_tokens),
-            text or (None if modifiers else SPECIAL_KEY_TO_CHARACTER.get(key, 
None)),
-        )
+            key_tokens.sort()
+            if key is not None:
+                key_tokens.append(key)
+            key_events.append(
+                events.Key(
+                    "+".join(key_tokens),
+                    text
+                    or (None if modifiers else 
SPECIAL_KEY_TO_CHARACTER.get(key, None)),
+                )
+            )
+        return key_events
 
     def _sequence_to_key_events(
         self, sequence: str, alt: bool = False
@@ -395,9 +422,10 @@
 
         if (
             not constants.DISABLE_KITTY_KEY
-            and (key := self._parse_extended_key(sequence)) is not None
+            and (keys := self._parse_extended_key(sequence)) is not None
         ):
-            yield key.copy()
+            for key in keys:
+                yield key.copy()
             return
 
         keys = ANSI_SEQUENCES_KEYS.get(sequence)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/textual-8.2.7/src/textual/pilot.py 
new/textual-8.2.8/src/textual/pilot.py
--- old/textual-8.2.7/src/textual/pilot.py      1970-01-01 01:00:00.000000000 
+0100
+++ new/textual-8.2.8/src/textual/pilot.py      1970-01-01 01:00:00.000000000 
+0100
@@ -438,7 +438,7 @@
         )
 
         offset = Offset(message_arguments["x"], message_arguments["y"])
-        if offset not in screen.region:
+        if offset not in screen.size.region:
             raise OutOfBounds(
                 "Target offset is outside of currently-visible screen region."
             )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/textual-8.2.7/src/textual/screen.py 
new/textual-8.2.8/src/textual/screen.py
--- old/textual-8.2.7/src/textual/screen.py     1970-01-01 01:00:00.000000000 
+0100
+++ new/textual-8.2.8/src/textual/screen.py     1970-01-01 01:00:00.000000000 
+0100
@@ -1838,8 +1838,11 @@
                     if select_offset is not None:
                         content_widget = select_widget
                         content_offset = select_offset
-                        assert isinstance(content_widget.parent, Widget)
-                        container = content_widget.parent
+                        container = (
+                            content_widget
+                            if isinstance(content_widget, Screen)
+                            else content_widget.parent
+                        )
                     else:
                         content_widget = None
                         container = select_widget
@@ -1884,6 +1887,7 @@
                 select_widget, select_offset = self.get_widget_and_offset_at(
                     event.x, event.y
                 )
+
                 if (
                     select_widget is not None
                     and select_widget.allow_select
@@ -1893,8 +1897,11 @@
                     if select_offset is not None:
                         content_widget = select_widget
                         content_offset = select_offset
-                        assert isinstance(content_widget.parent, Widget)
-                        container = content_widget.parent
+                        container = (
+                            content_widget
+                            if isinstance(content_widget, Screen)
+                            else content_widget.parent
+                        )
                     else:
                         content_widget = None
                         container = select_widget
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/textual-8.2.7/src/textual/widgets/_input.py 
new/textual-8.2.8/src/textual/widgets/_input.py
--- old/textual-8.2.7/src/textual/widgets/_input.py     1970-01-01 
01:00:00.000000000 +0100
+++ new/textual-8.2.8/src/textual/widgets/_input.py     1970-01-01 
01:00:00.000000000 +0100
@@ -122,9 +122,14 @@
         Binding(
             "ctrl+w", "delete_left_word", "Delete left to start of word", 
show=False
         ),
-        Binding("ctrl+u", "delete_left_all", "Delete all to the left", 
show=False),
         Binding(
-            "ctrl+backspace",
+            "ctrl+u,super+backspace",
+            "delete_left_all",
+            "Delete all to the left",
+            show=False,
+        ),
+        Binding(
+            "ctrl+backspace,alt+backspace",
             "delete_right_word",
             "Delete right to start of word",
             show=False,
@@ -145,6 +150,7 @@
     | shift+right | Move cursor right and select. |
     | ctrl+right | Move the cursor one word to the right. |
     | backspace | Delete the character to the left of the cursor. |
+    | ctrl+backspace,alt+backspace | Delete right to start of word. |
     | ctrl+shift+right | Move cursor right a word and select. |
     | ctrl+shift+a | Select all text in the input. |
     | home,ctrl+a | Go to the beginning of the input. |
@@ -154,7 +160,7 @@
     | delete,ctrl+d | Delete the character to the right of the cursor. |
     | enter | Submit the current value of the input. |
     | ctrl+w | Delete the word to the left of the cursor. |
-    | ctrl+u | Delete everything to the left of the cursor. |
+    | ctrl+u,super+backspace | Delete everything to the left of the cursor. |
     | ctrl+f | Delete the word to the right of the cursor. |
     | ctrl+k | Delete everything to the right of the cursor. |
     | ctrl+x | Cut selected text. |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/textual-8.2.7/src/textual/widgets/_text_area.py 
new/textual-8.2.8/src/textual/widgets/_text_area.py
--- old/textual-8.2.7/src/textual/widgets/_text_area.py 1970-01-01 
01:00:00.000000000 +0100
+++ new/textual-8.2.8/src/textual/widgets/_text_area.py 1970-01-01 
01:00:00.000000000 +0100
@@ -354,7 +354,7 @@
             show=False,
         ),
         Binding(
-            "ctrl+w,ctrl+backspace",
+            "ctrl+w,ctrl+backspace,alt+backspace",
             "delete_word_left",
             "Delete left to start of word",
             show=False,
@@ -390,7 +390,7 @@
             show=False,
         ),
         Binding(
-            "ctrl+u",
+            "ctrl+u,super+backspace",
             "delete_to_start_of_line",
             "Delete to line start",
             show=False,
@@ -446,7 +446,7 @@
     | delete,ctrl+d          | Delete character to the right of cursor.     |
     | alt+delete             | Delete from cursor to end of the word.       |
     | ctrl+shift+k           | Delete the current line.                     |
-    | ctrl+u                 | Delete from cursor to the start of the line. |
+    | ctrl+u,super+backspace | Delete from cursor to the start of the line. |
     | ctrl+k                 | Delete from cursor to the end of the line.   |
     | f6                     | Select the current line.                     |
     | f7                     | Select all text in the document.             |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/textual-8.2.7/tests/snapshot_tests/__snapshots__/test_snapshots/test_screen_padding_select.svg
 
new/textual-8.2.8/tests/snapshot_tests/__snapshots__/test_snapshots/test_screen_padding_select.svg
--- 
old/textual-8.2.7/tests/snapshot_tests/__snapshots__/test_snapshots/test_screen_padding_select.svg
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/textual-8.2.8/tests/snapshot_tests/__snapshots__/test_snapshots/test_screen_padding_select.svg
  1970-01-01 01:00:00.000000000 +0100
@@ -0,0 +1,150 @@
+<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-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-r1 { fill: #c5c8c6 }
+.terminal-r2 { fill: #e0e0e0 }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="584.5999999999999" />
+    </clipPath>
+    <clipPath id="terminal-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-7">
+    <rect x="0" y="172.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-8">
+    <rect x="0" y="196.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-9">
+    <rect x="0" y="221.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-10">
+    <rect x="0" y="245.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-11">
+    <rect x="0" y="269.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-12">
+    <rect x="0" y="294.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-13">
+    <rect x="0" y="318.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-14">
+    <rect x="0" y="343.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-15">
+    <rect x="0" y="367.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-16">
+    <rect x="0" y="391.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-17">
+    <rect x="0" y="416.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-18">
+    <rect x="0" y="440.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-19">
+    <rect x="0" y="465.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-20">
+    <rect x="0" y="489.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-21">
+    <rect x="0" y="513.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-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-title" 
fill="#c5c8c6" text-anchor="middle" x="496" y="27">CrashApp</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-clip-terminal)">
+    <rect fill="#121212" x="0" y="1.5" width="976" 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="#094472" x="12.2" 
y="25.9" width="61" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="73.2" y="25.9" width="707.6" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="780.8" y="25.9" 
width="195.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" 
x="0" y="50.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="74.7" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="99.1" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="147.9" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="172.3" width="976"
  height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="221.1" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="245.5" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="294.3" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="318.7" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="367.5" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="391.9" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill=
 "#121212" x="0" y="440.7" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="465.1" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="489.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="513.9" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="538.3" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="562.7" width="976" height="24.65" shape-rendering="crispEdges"/>
+    <g class="terminal-matrix">
+    <text class="terminal-r1" x="976" y="20" textLength="12.2" 
clip-path="url(#terminal-line-0)">
+</text><text class="terminal-r2" x="12.2" y="44.4" textLength="61" 
clip-path="url(#terminal-line-1)">Click</text><text class="terminal-r2" 
x="73.2" y="44.4" textLength="707.6" 
clip-path="url(#terminal-line-1)">&#160;or&#160;drag-select&#160;starting&#160;at&#160;the&#160;very&#160;left&#160;edge&#160;(column&#160;0).</text><text
 class="terminal-r1" x="976" y="44.4" textLength="12.2" 
clip-path="url(#terminal-line-1)">
+</text><text class="terminal-r1" x="976" y="68.8" textLength="12.2" 
clip-path="url(#terminal-line-2)">
+</text><text class="terminal-r1" x="976" y="93.2" textLength="12.2" 
clip-path="url(#terminal-line-3)">
+</text><text class="terminal-r1" x="976" y="117.6" textLength="12.2" 
clip-path="url(#terminal-line-4)">
+</text><text class="terminal-r1" x="976" y="142" textLength="12.2" 
clip-path="url(#terminal-line-5)">
+</text><text class="terminal-r1" x="976" y="166.4" textLength="12.2" 
clip-path="url(#terminal-line-6)">
+</text><text class="terminal-r1" x="976" y="190.8" textLength="12.2" 
clip-path="url(#terminal-line-7)">
+</text><text class="terminal-r1" x="976" y="215.2" textLength="12.2" 
clip-path="url(#terminal-line-8)">
+</text><text class="terminal-r1" x="976" y="239.6" textLength="12.2" 
clip-path="url(#terminal-line-9)">
+</text><text class="terminal-r1" x="976" y="264" textLength="12.2" 
clip-path="url(#terminal-line-10)">
+</text><text class="terminal-r1" x="976" y="288.4" textLength="12.2" 
clip-path="url(#terminal-line-11)">
+</text><text class="terminal-r1" x="976" y="312.8" textLength="12.2" 
clip-path="url(#terminal-line-12)">
+</text><text class="terminal-r1" x="976" y="337.2" textLength="12.2" 
clip-path="url(#terminal-line-13)">
+</text><text class="terminal-r1" x="976" y="361.6" textLength="12.2" 
clip-path="url(#terminal-line-14)">
+</text><text class="terminal-r1" x="976" y="386" textLength="12.2" 
clip-path="url(#terminal-line-15)">
+</text><text class="terminal-r1" x="976" y="410.4" textLength="12.2" 
clip-path="url(#terminal-line-16)">
+</text><text class="terminal-r1" x="976" y="434.8" textLength="12.2" 
clip-path="url(#terminal-line-17)">
+</text><text class="terminal-r1" x="976" y="459.2" textLength="12.2" 
clip-path="url(#terminal-line-18)">
+</text><text class="terminal-r1" x="976" y="483.6" textLength="12.2" 
clip-path="url(#terminal-line-19)">
+</text><text class="terminal-r1" x="976" y="508" textLength="12.2" 
clip-path="url(#terminal-line-20)">
+</text><text class="terminal-r1" x="976" y="532.4" textLength="12.2" 
clip-path="url(#terminal-line-21)">
+</text><text class="terminal-r1" x="976" y="556.8" textLength="12.2" 
clip-path="url(#terminal-line-22)">
+</text>
+    </g>
+    </g>
+</svg>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/textual-8.2.7/tests/snapshot_tests/__snapshots__/test_snapshots/test_screen_padding_select_move_down.svg
 
new/textual-8.2.8/tests/snapshot_tests/__snapshots__/test_snapshots/test_screen_padding_select_move_down.svg
--- 
old/textual-8.2.7/tests/snapshot_tests/__snapshots__/test_snapshots/test_screen_padding_select_move_down.svg
        1970-01-01 01:00:00.000000000 +0100
+++ 
new/textual-8.2.8/tests/snapshot_tests/__snapshots__/test_snapshots/test_screen_padding_select_move_down.svg
        1970-01-01 01:00:00.000000000 +0100
@@ -0,0 +1,150 @@
+<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-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-r1 { fill: #c5c8c6 }
+.terminal-r2 { fill: #e0e0e0 }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-clip-terminal">
+      <rect x="0" y="0" width="975.0" height="584.5999999999999" />
+    </clipPath>
+    <clipPath id="terminal-line-0">
+    <rect x="0" y="1.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-1">
+    <rect x="0" y="25.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-2">
+    <rect x="0" y="50.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-3">
+    <rect x="0" y="74.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-4">
+    <rect x="0" y="99.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-5">
+    <rect x="0" y="123.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-6">
+    <rect x="0" y="147.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-7">
+    <rect x="0" y="172.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-8">
+    <rect x="0" y="196.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-9">
+    <rect x="0" y="221.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-10">
+    <rect x="0" y="245.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-11">
+    <rect x="0" y="269.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-12">
+    <rect x="0" y="294.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-13">
+    <rect x="0" y="318.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-14">
+    <rect x="0" y="343.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-15">
+    <rect x="0" y="367.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-16">
+    <rect x="0" y="391.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-17">
+    <rect x="0" y="416.3" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-18">
+    <rect x="0" y="440.7" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-19">
+    <rect x="0" y="465.1" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-20">
+    <rect x="0" y="489.5" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-line-21">
+    <rect x="0" y="513.9" width="976" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-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-title" 
fill="#c5c8c6" text-anchor="middle" x="496" y="27">CrashApp</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-clip-terminal)">
+    <rect fill="#121212" x="0" y="1.5" width="976" 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="#094472" x="12.2" 
y="25.9" width="183" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="195.2" y="25.9" width="780.8" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="50.3" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="74.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="99.1" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="123.5" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="172.3" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="196.7" width="976" 
he
 ight="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="245.5" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="269.9" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="318.7" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="343.1" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="391.9" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="416.3" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#1
 21212" x="0" y="465.1" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="489.5" width="976" 
height="24.65" shape-rendering="crispEdges"/><rect fill="#121212" x="0" 
y="513.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect 
fill="#121212" x="0" y="538.3" width="976" height="24.65" 
shape-rendering="crispEdges"/><rect fill="#121212" x="0" y="562.7" width="976" 
height="24.65" shape-rendering="crispEdges"/>
+    <g class="terminal-matrix">
+    <text class="terminal-r1" x="976" y="20" textLength="12.2" 
clip-path="url(#terminal-line-0)">
+</text><text class="terminal-r2" x="12.2" y="44.4" textLength="183" 
clip-path="url(#terminal-line-1)">Call&#160;me&#160;Ishmael</text><text 
class="terminal-r1" x="976" y="44.4" textLength="12.2" 
clip-path="url(#terminal-line-1)">
+</text><text class="terminal-r1" x="976" y="68.8" textLength="12.2" 
clip-path="url(#terminal-line-2)">
+</text><text class="terminal-r1" x="976" y="93.2" textLength="12.2" 
clip-path="url(#terminal-line-3)">
+</text><text class="terminal-r1" x="976" y="117.6" textLength="12.2" 
clip-path="url(#terminal-line-4)">
+</text><text class="terminal-r1" x="976" y="142" textLength="12.2" 
clip-path="url(#terminal-line-5)">
+</text><text class="terminal-r1" x="976" y="166.4" textLength="12.2" 
clip-path="url(#terminal-line-6)">
+</text><text class="terminal-r1" x="976" y="190.8" textLength="12.2" 
clip-path="url(#terminal-line-7)">
+</text><text class="terminal-r1" x="976" y="215.2" textLength="12.2" 
clip-path="url(#terminal-line-8)">
+</text><text class="terminal-r1" x="976" y="239.6" textLength="12.2" 
clip-path="url(#terminal-line-9)">
+</text><text class="terminal-r1" x="976" y="264" textLength="12.2" 
clip-path="url(#terminal-line-10)">
+</text><text class="terminal-r1" x="976" y="288.4" textLength="12.2" 
clip-path="url(#terminal-line-11)">
+</text><text class="terminal-r1" x="976" y="312.8" textLength="12.2" 
clip-path="url(#terminal-line-12)">
+</text><text class="terminal-r1" x="976" y="337.2" textLength="12.2" 
clip-path="url(#terminal-line-13)">
+</text><text class="terminal-r1" x="976" y="361.6" textLength="12.2" 
clip-path="url(#terminal-line-14)">
+</text><text class="terminal-r1" x="976" y="386" textLength="12.2" 
clip-path="url(#terminal-line-15)">
+</text><text class="terminal-r1" x="976" y="410.4" textLength="12.2" 
clip-path="url(#terminal-line-16)">
+</text><text class="terminal-r1" x="976" y="434.8" textLength="12.2" 
clip-path="url(#terminal-line-17)">
+</text><text class="terminal-r1" x="976" y="459.2" textLength="12.2" 
clip-path="url(#terminal-line-18)">
+</text><text class="terminal-r1" x="976" y="483.6" textLength="12.2" 
clip-path="url(#terminal-line-19)">
+</text><text class="terminal-r1" x="976" y="508" textLength="12.2" 
clip-path="url(#terminal-line-20)">
+</text><text class="terminal-r1" x="976" y="532.4" textLength="12.2" 
clip-path="url(#terminal-line-21)">
+</text><text class="terminal-r1" x="976" y="556.8" textLength="12.2" 
clip-path="url(#terminal-line-22)">
+</text>
+    </g>
+    </g>
+</svg>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/textual-8.2.7/tests/snapshot_tests/test_snapshots.py 
new/textual-8.2.8/tests/snapshot_tests/test_snapshots.py
--- old/textual-8.2.7/tests/snapshot_tests/test_snapshots.py    1970-01-01 
01:00:00.000000000 +0100
+++ new/textual-8.2.8/tests/snapshot_tests/test_snapshots.py    1970-01-01 
01:00:00.000000000 +0100
@@ -4936,3 +4936,61 @@
                 label.styles.text_opacity = f"{n * 10}%"
 
     assert snap_compare(TApp())
+
+
+def test_screen_padding_select(snap_compare) -> None:
+    """Regression test for https://github.com/Textualize/textual/issues/6596
+
+    When a screen has padding, clicking in the padding area should not crash
+
+    You should see a sentence at the top of the screen, indented one cell in, 
and the first word (Click) highlighted.
+    """
+
+    class CrashApp(App):
+        # Padding on the Screen creates a 1-cell gutter at column 0 that 
belongs to
+        # the Screen itself — no child widget renders there.
+        CSS = "Screen { padding: 1 1 }"
+
+        def compose(self) -> ComposeResult:
+            yield Static(
+                "Click or drag-select starting at the very left edge (column 
0)."
+            )
+
+    async def run_before(pilot: Pilot) -> None:
+        await pilot.mouse_down(offset=(0, 1))
+        await pilot.mouse_up(offset=(6, 1))
+        await pilot.pause()
+        selected_text = pilot.app.screen.get_selected_text()
+        assert selected_text == "Click"
+
+    assert snap_compare(CrashApp(), run_before=run_before)
+
+
+def test_screen_padding_select_move_down(snap_compare) -> None:
+    """Regression test for https://github.com/Textualize/textual/issues/6596
+
+    When a screen has padding, clicking in the padding area should not crash.
+
+    Check click and drag down.
+
+    You should see a selected sentence at the top of the screen.
+    """
+
+    SENTENCE = "Call me Ishmael"
+
+    class CrashApp(App):
+        # Padding on the Screen creates a 1-cell gutter at column 0 that 
belongs to
+        # the Screen itself — no child widget renders there.
+        CSS = "Screen { padding: 1 1 }"
+
+        def compose(self) -> ComposeResult:
+            yield Static(SENTENCE)
+
+    async def run_before(pilot: Pilot) -> None:
+        await pilot.mouse_down(offset=(10, 0))
+        await pilot.mouse_up(offset=(10, 5))
+        await pilot.pause()
+        selected_text = pilot.app.screen.get_selected_text()
+        assert selected_text == SENTENCE
+
+    assert snap_compare(CrashApp(), run_before=run_before)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/textual-8.2.7/tests/test_xterm_parser.py 
new/textual-8.2.8/tests/test_xterm_parser.py
--- old/textual-8.2.7/tests/test_xterm_parser.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/textual-8.2.8/tests/test_xterm_parser.py        1970-01-01 
01:00:00.000000000 +0100
@@ -212,6 +212,26 @@
 
 
 @pytest.mark.parametrize(
+    "sequence,keys",
+    [
+        ("a", "a"),  # Sanity check
+        (
+            "\x1b[58;2;126:47u",
+            "~/",
+        ),  # press option+n folloed by forwards slash, emits ~/ on 
international keyboards
+    ],
+)
+def test_extended_keys(parser, sequence: str, keys: str) -> None:
+    """Test kitty key sequences that produce multiple keys."""
+    events = []
+    for event in parser.feed(sequence):
+        assert isinstance(event, Key)
+        events.append(event)
+    parsed_keys = "".join([event.character for event in events])
+    assert parsed_keys == keys
+
+
[email protected](
     "sequence, event_type, shift, meta",
     [
         # Mouse down, with and without modifiers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/textual-8.2.7/tests/text_area/test_edit_via_bindings.py 
new/textual-8.2.8/tests/text_area/test_edit_via_bindings.py
--- old/textual-8.2.7/tests/text_area/test_edit_via_bindings.py 1970-01-01 
01:00:00.000000000 +0100
+++ new/textual-8.2.8/tests/text_area/test_edit_via_bindings.py 1970-01-01 
01:00:00.000000000 +0100
@@ -373,6 +373,18 @@
         assert text_area.text == expected_result
         assert text_area.selection == final_selection
 
+    # Repeat with alt+backspace (alias) binding
+    app = TextAreaApp()
+    async with app.run_test() as pilot:
+        text_area = app.query_one(TextArea)
+        text_area.load_text("  012 345 6789")
+        text_area.selection = selection
+
+        await pilot.press("ctrl+backspace")
+
+        assert text_area.text == expected_result
+        assert text_area.selection == final_selection
+
 
 @pytest.mark.parametrize(
     "selection,expected_result,final_selection",
@@ -578,7 +590,14 @@
         assert text_area.text == TEXT  # No change
 
 
-async def test_delete_to_start_of_line_deletes_newline():
[email protected](
+    "hotkey",
+    [
+        "ctrl+u",
+        "super+backspace",
+    ],
+)
+async def test_delete_to_start_of_line_deletes_newline(hotkey: str):
     """Test that default ctrl+u deletes newline when the cursor is at the 
start"""
 
     class TextAreaApp(App):
@@ -596,17 +615,17 @@
         assert text_area.text == "Hello\nWorld"
 
         # Delete the line
-        await pilot.press("ctrl+u")
+        await pilot.press(hotkey)
         assert text_area.text == "Hello\n"
 
         # Delete at start, deletes the \n
-        await pilot.press("ctrl+u")
+        await pilot.press(hotkey)
         assert text_area.text == "Hello"
 
         # Delete the line
-        await pilot.press("ctrl+u")
+        await pilot.press(hotkey)
         assert text_area.text == ""
 
         # Delete empty is a NOOP
-        await pilot.press("ctrl+u")
+        await pilot.press(hotkey)
         assert text_area.text == ""

Reply via email to