We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Docs change - use `pane` instead of `tab` for an event listener on tab change
We tried adding an event listener for a tab change using the docs here, but couldn't get it to work: https://textual.textualize.io/guide/events/#applying-css-selectors-to-arbitrary-attributes. Should the docs be updated to use `pane` instead of `tab`, or are we doing something wrong?  Specifically this snippet:

```python
@on(TabbedContent.TabActivated, tab="#home")
def home_tab(self) -> None:
    self.log("Switched back to home tab.")
```

I got it working, I think it's related to the breaking change described in https://github.com/Textualize/textual/blob/main/CHANGELOG.md#0460---2023-12-17

> Breaking change: tab is no longer a @on decorator selector for TabbedContent.TabActivated -- use pane instead https://github.com/Textualize/textual/pull/3815

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/textual/_on.py`
Content:
```
1 from __future__ import annotations
2 
3 from typing import Callable, TypeVar
4 
5 from .css.model import SelectorSet
6 from .css.parse import parse_selectors
7 from .css.tokenizer import TokenError
8 from .message import Message
9 
10 DecoratedType = TypeVar("DecoratedType")
11 
12 
13 class OnDecoratorError(Exception):
14     """Errors related to the `on` decorator.
15 
16     Typically raised at import time as an early warning system.
17     """
18 
19 
20 class OnNoWidget(Exception):
21     """A selector was applied to an attribute that isn't a widget."""
22 
23 
24 def on(
25     message_type: type[Message], selector: str | None = None, **kwargs: str
26 ) -> Callable[[DecoratedType], DecoratedType]:
27     """Decorator to declare that the method is a message handler.
28 
29     The decorator accepts an optional CSS selector that will be matched against a widget exposed by
30     a `control` property on the message.
31 
32     Example:
33         ```python
34         # Handle the press of buttons with ID "#quit".
35         @on(Button.Pressed, "#quit")
36         def quit_button(self) -> None:
37             self.app.quit()
38         ```
39 
40     Keyword arguments can be used to match additional selectors for attributes
41     listed in [`ALLOW_SELECTOR_MATCH`][textual.message.Message.ALLOW_SELECTOR_MATCH].
42 
43     Example:
44         ```python
45         # Handle the activation of the tab "#home" within the `TabbedContent` "#tabs".
46         @on(TabbedContent.TabActivated, "#tabs", tab="#home")
47         def switch_to_home(self) -> None:
48             self.log("Switching back to the home tab.")
49             ...
50         ```
51 
52     Args:
53         message_type: The message type (i.e. the class).
54         selector: An optional [selector](/guide/CSS#selectors). If supplied, the handler will only be called if `selector`
55             matches the widget from the `control` attribute of the message.
56         **kwargs: Additional selectors for other attributes of the message.
57     """
58 
59     selectors: dict[str, str] = {}
60     if selector is not None:
61         selectors["control"] = selector
62     if kwargs:
63         selectors.update(kwargs)
64 
65     parsed_selectors: dict[str, tuple[SelectorSet, ...]] = {}
66     for attribute, css_selector in selectors.items():
67         if attribute == "control":
68             if message_type.control == Message.control:
69                 raise OnDecoratorError(
70                     "The message class must have a 'control' to match with the on decorator"
71                 )
72         elif attribute not in message_type.ALLOW_SELECTOR_MATCH:
73             raise OnDecoratorError(
74                 f"The attribute {attribute!r} can't be matched; have you added it to "
75                 + f"{message_type.__name__}.ALLOW_SELECTOR_MATCH?"
76             )
77         try:
78             parsed_selectors[attribute] = parse_selectors(css_selector)
79         except TokenError:
80             raise OnDecoratorError(
81                 f"Unable to parse selector {css_selector!r} for {attribute}; check for syntax errors"
82             ) from None
83 
84     def decorator(method: DecoratedType) -> DecoratedType:
85         """Store message and selector in function attribute, return callable unaltered."""
86 
87         if not hasattr(method, "_textual_on"):
88             setattr(method, "_textual_on", [])
89         getattr(method, "_textual_on").append((message_type, parsed_selectors))
90 
91         return method
92 
93     return decorator
94
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/textual/_on.py b/src/textual/_on.py
--- a/src/textual/_on.py
+++ b/src/textual/_on.py
@@ -43,7 +43,7 @@
     Example:
         ```python
         # Handle the activation of the tab "#home" within the `TabbedContent` "#tabs".
-        @on(TabbedContent.TabActivated, "#tabs", tab="#home")
+        @on(TabbedContent.TabActivated, "#tabs", pane="#home")
         def switch_to_home(self) -> None:
             self.log("Switching back to the home tab.")
             ...

{"golden_diff": "diff --git a/src/textual/_on.py b/src/textual/_on.py\n--- a/src/textual/_on.py\n+++ b/src/textual/_on.py\n@@ -43,7 +43,7 @@\n     Example:\n         ```python\n         # Handle the activation of the tab \"#home\" within the `TabbedContent` \"#tabs\".\n-        @on(TabbedContent.TabActivated, \"#tabs\", tab=\"#home\")\n+        @on(TabbedContent.TabActivated, \"#tabs\", pane=\"#home\")\n         def switch_to_home(self) -> None:\n             self.log(\"Switching back to the home tab.\")\n             ...\n", "issue": "Docs change - use `pane` instead of `tab` for an event listener on tab change\nWe tried adding an event listener for a tab change using the docs here, but couldn't get it to work: https://textual.textualize.io/guide/events/#applying-css-selectors-to-arbitrary-attributes. Should the docs be updated to use `pane` instead of `tab`, or are we doing something wrong?  Specifically this snippet:\r\n\r\n```python\r\n@on(TabbedContent.TabActivated, tab=\"#home\")\r\ndef home_tab(self) -> None:\r\n    self.log(\"Switched back to home tab.\")\r\n```\r\n\r\nI got it working, I think it's related to the breaking change described in https://github.com/Textualize/textual/blob/main/CHANGELOG.md#0460---2023-12-17\r\n\r\n> Breaking change: tab is no longer a @on decorator selector for TabbedContent.TabActivated -- use pane instead https://github.com/Textualize/textual/pull/3815\n", "before_files": [{"content": "from __future__ import annotations\n\nfrom typing import Callable, TypeVar\n\nfrom .css.model import SelectorSet\nfrom .css.parse import parse_selectors\nfrom .css.tokenizer import TokenError\nfrom .message import Message\n\nDecoratedType = TypeVar(\"DecoratedType\")\n\n\nclass OnDecoratorError(Exception):\n    \"\"\"Errors related to the `on` decorator.\n\n    Typically raised at import time as an early warning system.\n    \"\"\"\n\n\nclass OnNoWidget(Exception):\n    \"\"\"A selector was applied to an attribute that isn't a widget.\"\"\"\n\n\ndef on(\n    message_type: type[Message], selector: str | None = None, **kwargs: str\n) -> Callable[[DecoratedType], DecoratedType]:\n    \"\"\"Decorator to declare that the method is a message handler.\n\n    The decorator accepts an optional CSS selector that will be matched against a widget exposed by\n    a `control` property on the message.\n\n    Example:\n        ```python\n        # Handle the press of buttons with ID \"#quit\".\n        @on(Button.Pressed, \"#quit\")\n        def quit_button(self) -> None:\n            self.app.quit()\n        ```\n\n    Keyword arguments can be used to match additional selectors for attributes\n    listed in [`ALLOW_SELECTOR_MATCH`][textual.message.Message.ALLOW_SELECTOR_MATCH].\n\n    Example:\n        ```python\n        # Handle the activation of the tab \"#home\" within the `TabbedContent` \"#tabs\".\n        @on(TabbedContent.TabActivated, \"#tabs\", tab=\"#home\")\n        def switch_to_home(self) -> None:\n            self.log(\"Switching back to the home tab.\")\n            ...\n        ```\n\n    Args:\n        message_type: The message type (i.e. the class).\n        selector: An optional [selector](/guide/CSS#selectors). If supplied, the handler will only be called if `selector`\n            matches the widget from the `control` attribute of the message.\n        **kwargs: Additional selectors for other attributes of the message.\n    \"\"\"\n\n    selectors: dict[str, str] = {}\n    if selector is not None:\n        selectors[\"control\"] = selector\n    if kwargs:\n        selectors.update(kwargs)\n\n    parsed_selectors: dict[str, tuple[SelectorSet, ...]] = {}\n    for attribute, css_selector in selectors.items():\n        if attribute == \"control\":\n            if message_type.control == Message.control:\n                raise OnDecoratorError(\n                    \"The message class must have a 'control' to match with the on decorator\"\n                )\n        elif attribute not in message_type.ALLOW_SELECTOR_MATCH:\n            raise OnDecoratorError(\n                f\"The attribute {attribute!r} can't be matched; have you added it to \"\n                + f\"{message_type.__name__}.ALLOW_SELECTOR_MATCH?\"\n            )\n        try:\n            parsed_selectors[attribute] = parse_selectors(css_selector)\n        except TokenError:\n            raise OnDecoratorError(\n                f\"Unable to parse selector {css_selector!r} for {attribute}; check for syntax errors\"\n            ) from None\n\n    def decorator(method: DecoratedType) -> DecoratedType:\n        \"\"\"Store message and selector in function attribute, return callable unaltered.\"\"\"\n\n        if not hasattr(method, \"_textual_on\"):\n            setattr(method, \"_textual_on\", [])\n        getattr(method, \"_textual_on\").append((message_type, parsed_selectors))\n\n        return method\n\n    return decorator\n", "path": "src/textual/_on.py"}], "after_files": [{"content": "from __future__ import annotations\n\nfrom typing import Callable, TypeVar\n\nfrom .css.model import SelectorSet\nfrom .css.parse import parse_selectors\nfrom .css.tokenizer import TokenError\nfrom .message import Message\n\nDecoratedType = TypeVar(\"DecoratedType\")\n\n\nclass OnDecoratorError(Exception):\n    \"\"\"Errors related to the `on` decorator.\n\n    Typically raised at import time as an early warning system.\n    \"\"\"\n\n\nclass OnNoWidget(Exception):\n    \"\"\"A selector was applied to an attribute that isn't a widget.\"\"\"\n\n\ndef on(\n    message_type: type[Message], selector: str | None = None, **kwargs: str\n) -> Callable[[DecoratedType], DecoratedType]:\n    \"\"\"Decorator to declare that the method is a message handler.\n\n    The decorator accepts an optional CSS selector that will be matched against a widget exposed by\n    a `control` property on the message.\n\n    Example:\n        ```python\n        # Handle the press of buttons with ID \"#quit\".\n        @on(Button.Pressed, \"#quit\")\n        def quit_button(self) -> None:\n            self.app.quit()\n        ```\n\n    Keyword arguments can be used to match additional selectors for attributes\n    listed in [`ALLOW_SELECTOR_MATCH`][textual.message.Message.ALLOW_SELECTOR_MATCH].\n\n    Example:\n        ```python\n        # Handle the activation of the tab \"#home\" within the `TabbedContent` \"#tabs\".\n        @on(TabbedContent.TabActivated, \"#tabs\", pane=\"#home\")\n        def switch_to_home(self) -> None:\n            self.log(\"Switching back to the home tab.\")\n            ...\n        ```\n\n    Args:\n        message_type: The message type (i.e. the class).\n        selector: An optional [selector](/guide/CSS#selectors). If supplied, the handler will only be called if `selector`\n            matches the widget from the `control` attribute of the message.\n        **kwargs: Additional selectors for other attributes of the message.\n    \"\"\"\n\n    selectors: dict[str, str] = {}\n    if selector is not None:\n        selectors[\"control\"] = selector\n    if kwargs:\n        selectors.update(kwargs)\n\n    parsed_selectors: dict[str, tuple[SelectorSet, ...]] = {}\n    for attribute, css_selector in selectors.items():\n        if attribute == \"control\":\n            if message_type.control == Message.control:\n                raise OnDecoratorError(\n                    \"The message class must have a 'control' to match with the on decorator\"\n                )\n        elif attribute not in message_type.ALLOW_SELECTOR_MATCH:\n            raise OnDecoratorError(\n                f\"The attribute {attribute!r} can't be matched; have you added it to \"\n                + f\"{message_type.__name__}.ALLOW_SELECTOR_MATCH?\"\n            )\n        try:\n            parsed_selectors[attribute] = parse_selectors(css_selector)\n        except TokenError:\n            raise OnDecoratorError(\n                f\"Unable to parse selector {css_selector!r} for {attribute}; check for syntax errors\"\n            ) from None\n\n    def decorator(method: DecoratedType) -> DecoratedType:\n        \"\"\"Store message and selector in function attribute, return callable unaltered.\"\"\"\n\n        if not hasattr(method, \"_textual_on\"):\n            setattr(method, \"_textual_on\", [])\n        getattr(method, \"_textual_on\").append((message_type, parsed_selectors))\n\n        return method\n\n    return decorator\n", "path": "src/textual/_on.py"}]}