We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Writing to a hidden RichLog breaks table alignment
I've got a `RichLog` that is often hidden and I write text to it. I'm using a `Rich.Table` to left/right justify two pieces of text on a single line.

This works as expected while the widget is shown.

If the widget has either `visible=False` or `display=False` set, then any text written during that time is collapsed together into a single string.

I'm not sure if this is a side effect of not having the widget displayed (no space is calculated for it so it doesn't know what sizes to use when writing the `Table`?) or not. If this is working as expected, might there be any way to write to the widget while it's hidden but preserve the appropriate sizing?

The following is an example of writing to the widget both while it is shown, has `visible=False` and `display=False`.

![hidden-width](https://github.com/Textualize/textual/assets/11033703/1cc78911-f193-4186-9b6a-7fc0e0246bf5)

Example Code (same that produced `gif` above).
```python
from __future__ import annotations

import random

from rich.align import Align
from rich.table import Table
from rich.text import Text
from textual.app import App, ComposeResult
from textual.widgets import RichLog


class Example(App):
    BINDINGS = [
        ("w", "write_line", "Write Line"),
        ("h", "toggle_visible", "Toggle Visibility"),
        ("d", "toggle_display", "Toggle Display"),
        ("q", "quit", "Quit"),
    ]

    DEFAULT_CSS = """
    #one  {
        border: round white;
        height: 1fr;
    }
    #two  {
        border: round white;
        height: 1fr;
    }
    """

    def compose(self) -> ComposeResult:
        log1 = RichLog(id="one")
        log1.border_title = "Log 1"
        log2 = RichLog(id="two")
        log2.border_title = "Log 2"
        yield log1
        yield log2

    def action_toggle_visible(self) -> None:
        log1 = self.query_one("#one", RichLog)
        log2 = self.query_one("#two", RichLog)

        log2.visible = not log2.visible
        log1.write(f"log2 visibility={log2.visible}")

    def action_toggle_display(self) -> None:
        log1 = self.query_one("#one", RichLog)
        log2 = self.query_one("#two", RichLog)

        log2.display = not log2.display
        log1.write(f"log2 display={log2.display}")

    def action_write_line(self) -> None:
        """Write a line to log 2"""
        log1 = self.query_one("#one", RichLog)
        log2 = self.query_one("#two", RichLog)

        left = Text("something: text on the left")
        right = Text("Stuff on the right")
        table = Table.grid(expand=True)
        table.add_row(left, Align.right(right))

        log2.write(table, expand=True)
        log1.write("Wrote to log2")


if __name__ == "__main__":
    app = Example()
    app.run()
```

# Textual Diagnostics

## Versions

| Name    | Value  |
|---------|--------|
| Textual | 0.46.0 |
| Rich    | 13.7.0 |

## Python

| Name           | Value                                                     |
|----------------|-----------------------------------------------------------|
| Version        | 3.8.10                                                    |
| Implementation | CPython                                                   |
| Compiler       | Clang 14.0.3 (clang-1403.0.22.14.1)                       |
| Executable     | /Users/geoff/Documents/projects/kafka-tui/venv/bin/python |

## Operating System

| Name    | Value                                                                                                 |
|---------|-------------------------------------------------------------------------------------------------------|
| System  | Darwin                                                                                                |
| Release | 23.3.0                                                                                                |
| Version | Darwin Kernel Version 23.3.0: Wed Dec 20 21:30:27 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T8103 |

## Terminal

| Name                 | Value           |
|----------------------|-----------------|
| Terminal Application | vscode (1.86.0) |
| TERM                 | xterm-256color  |
| COLORTERM            | truecolor       |
| FORCE_COLOR          | *Not set*       |
| NO_COLOR             | *Not set*       |

## Rich Console options

| Name           | Value                |
|----------------|----------------------|
| size           | width=115, height=40 |
| legacy_windows | False                |
| min_width      | 1                    |
| max_width      | 115                  |
| is_terminal    | True                 |
| encoding       | utf-8                |
| max_height     | 40                   |
| justify        | None                 |
| overflow       | None                 |
| no_wrap        | False                |
| highlight      | None                 |
| markup         | None                 |
| height         | None                 |

Feel free to add screenshots and / or videos. These can be very helpful!


--- 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/widgets/_rich_log.py`
Content:
```
1 """Provides a scrollable text-logging widget."""
2 
3 from __future__ import annotations
4 
5 from typing import TYPE_CHECKING, Optional, cast
6 
7 from rich.console import RenderableType
8 from rich.highlighter import ReprHighlighter
9 from rich.measure import measure_renderables
10 from rich.pretty import Pretty
11 from rich.protocol import is_renderable
12 from rich.segment import Segment
13 from rich.text import Text
14 
15 from ..cache import LRUCache
16 from ..geometry import Region, Size
17 from ..reactive import var
18 from ..scroll_view import ScrollView
19 from ..strip import Strip
20 
21 if TYPE_CHECKING:
22     from typing_extensions import Self
23 
24 
25 class RichLog(ScrollView, can_focus=True):
26     """A widget for logging text."""
27 
28     DEFAULT_CSS = """
29     RichLog{
30         background: $surface;
31         color: $text;
32         overflow-y: scroll;
33     }
34     """
35 
36     max_lines: var[int | None] = var[Optional[int]](None)
37     min_width: var[int] = var(78)
38     wrap: var[bool] = var(False)
39     highlight: var[bool] = var(False)
40     markup: var[bool] = var(False)
41     auto_scroll: var[bool] = var(True)
42 
43     def __init__(
44         self,
45         *,
46         max_lines: int | None = None,
47         min_width: int = 78,
48         wrap: bool = False,
49         highlight: bool = False,
50         markup: bool = False,
51         auto_scroll: bool = True,
52         name: str | None = None,
53         id: str | None = None,
54         classes: str | None = None,
55         disabled: bool = False,
56     ) -> None:
57         """Create a RichLog widget.
58 
59         Args:
60             max_lines: Maximum number of lines in the log or `None` for no maximum.
61             min_width: Minimum width of renderables.
62             wrap: Enable word wrapping (default is off).
63             highlight: Automatically highlight content.
64             markup: Apply Rich console markup.
65             auto_scroll: Enable automatic scrolling to end.
66             name: The name of the text log.
67             id: The ID of the text log in the DOM.
68             classes: The CSS classes of the text log.
69             disabled: Whether the text log is disabled or not.
70         """
71         super().__init__(name=name, id=id, classes=classes, disabled=disabled)
72         self.max_lines = max_lines
73         """Maximum number of lines in the log or `None` for no maximum."""
74         self._start_line: int = 0
75         self.lines: list[Strip] = []
76         self._line_cache: LRUCache[tuple[int, int, int, int], Strip]
77         self._line_cache = LRUCache(1024)
78         self.max_width: int = 0
79         self.min_width = min_width
80         """Minimum width of renderables."""
81         self.wrap = wrap
82         """Enable word wrapping."""
83         self.highlight = highlight
84         """Automatically highlight content."""
85         self.markup = markup
86         """Apply Rich console markup."""
87         self.auto_scroll = auto_scroll
88         """Automatically scroll to the end on write."""
89         self.highlighter = ReprHighlighter()
90 
91     def notify_style_update(self) -> None:
92         self._line_cache.clear()
93 
94     def _make_renderable(self, content: RenderableType | object) -> RenderableType:
95         """Make content renderable.
96 
97         Args:
98             content: Content to render.
99 
100         Returns:
101             A Rich renderable.
102         """
103         renderable: RenderableType
104         if not is_renderable(content):
105             renderable = Pretty(content)
106         else:
107             if isinstance(content, str):
108                 if self.markup:
109                     renderable = Text.from_markup(content)
110                 else:
111                     renderable = Text(content)
112                 if self.highlight:
113                     renderable = self.highlighter(renderable)
114             else:
115                 renderable = cast(RenderableType, content)
116 
117         if isinstance(renderable, Text):
118             renderable.expand_tabs()
119 
120         return renderable
121 
122     def write(
123         self,
124         content: RenderableType | object,
125         width: int | None = None,
126         expand: bool = False,
127         shrink: bool = True,
128         scroll_end: bool | None = None,
129     ) -> Self:
130         """Write text or a rich renderable.
131 
132         Args:
133             content: Rich renderable (or text).
134             width: Width to render or `None` to use optimal width.
135             expand: Enable expand to widget width, or `False` to use `width`.
136             shrink: Enable shrinking of content to fit width.
137             scroll_end: Enable automatic scroll to end, or `None` to use `self.auto_scroll`.
138 
139         Returns:
140             The `RichLog` instance.
141         """
142 
143         auto_scroll = self.auto_scroll if scroll_end is None else scroll_end
144 
145         console = self.app.console
146         render_options = console.options
147 
148         renderable = self._make_renderable(content)
149 
150         if isinstance(renderable, Text) and not self.wrap:
151             render_options = render_options.update(overflow="ignore", no_wrap=True)
152 
153         render_width = measure_renderables(
154             console, render_options, [renderable]
155         ).maximum
156         container_width = (
157             self.scrollable_content_region.width if width is None else width
158         )
159         if container_width:
160             if expand and render_width < container_width:
161                 render_width = container_width
162             if shrink and render_width > container_width:
163                 render_width = container_width
164 
165         segments = self.app.console.render(
166             renderable, render_options.update_width(render_width)
167         )
168         lines = list(Segment.split_lines(segments))
169         if not lines:
170             self.lines.append(Strip.blank(render_width))
171         else:
172             self.max_width = max(
173                 self.max_width,
174                 max(sum([segment.cell_length for segment in _line]) for _line in lines),
175             )
176             strips = Strip.from_lines(lines)
177             for strip in strips:
178                 strip.adjust_cell_length(render_width)
179             self.lines.extend(strips)
180 
181             if self.max_lines is not None and len(self.lines) > self.max_lines:
182                 self._start_line += len(self.lines) - self.max_lines
183                 self.refresh()
184                 self.lines = self.lines[-self.max_lines :]
185         self.virtual_size = Size(self.max_width, len(self.lines))
186         if auto_scroll:
187             self.scroll_end(animate=False)
188 
189         return self
190 
191     def clear(self) -> Self:
192         """Clear the text log.
193 
194         Returns:
195             The `RichLog` instance.
196         """
197         self.lines.clear()
198         self._line_cache.clear()
199         self._start_line = 0
200         self.max_width = 0
201         self.virtual_size = Size(self.max_width, len(self.lines))
202         self.refresh()
203         return self
204 
205     def render_line(self, y: int) -> Strip:
206         scroll_x, scroll_y = self.scroll_offset
207         line = self._render_line(scroll_y + y, scroll_x, self.size.width)
208         strip = line.apply_style(self.rich_style)
209         return strip
210 
211     def render_lines(self, crop: Region) -> list[Strip]:
212         """Render the widget in to lines.
213 
214         Args:
215             crop: Region within visible area to.
216 
217         Returns:
218             A list of list of segments.
219         """
220         lines = self._styles_cache.render_widget(self, crop)
221         return lines
222 
223     def _render_line(self, y: int, scroll_x: int, width: int) -> Strip:
224         if y >= len(self.lines):
225             return Strip.blank(width, self.rich_style)
226 
227         key = (y + self._start_line, scroll_x, width, self.max_width)
228         if key in self._line_cache:
229             return self._line_cache[key]
230 
231         line = self.lines[y].crop_extend(scroll_x, scroll_x + width, self.rich_style)
232 
233         self._line_cache[key] = line
234         return line
235
```
--- 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/widgets/_rich_log.py b/src/textual/widgets/_rich_log.py
--- a/src/textual/widgets/_rich_log.py
+++ b/src/textual/widgets/_rich_log.py
@@ -88,9 +88,15 @@
         """Automatically scroll to the end on write."""
         self.highlighter = ReprHighlighter()
 
+        self._last_container_width: int = min_width
+        """Record the last width we rendered content at."""
+
     def notify_style_update(self) -> None:
         self._line_cache.clear()
 
+    def on_resize(self) -> None:
+        self._last_container_width = self.scrollable_content_region.width
+
     def _make_renderable(self, content: RenderableType | object) -> RenderableType:
         """Make content renderable.
 
@@ -153,14 +159,22 @@
         render_width = measure_renderables(
             console, render_options, [renderable]
         ).maximum
+
         container_width = (
             self.scrollable_content_region.width if width is None else width
         )
-        if container_width:
-            if expand and render_width < container_width:
-                render_width = container_width
-            if shrink and render_width > container_width:
-                render_width = container_width
+
+        # Use the container_width if it's available, otherwise use the last available width.
+        container_width = (
+            container_width if container_width else self._last_container_width
+        )
+
+        if expand and render_width < container_width:
+            render_width = container_width
+        if shrink and render_width > container_width:
+            render_width = container_width
+
+        render_width = max(render_width, self.min_width)
 
         segments = self.app.console.render(
             renderable, render_options.update_width(render_width)

{"golden_diff": "diff --git a/src/textual/widgets/_rich_log.py b/src/textual/widgets/_rich_log.py\n--- a/src/textual/widgets/_rich_log.py\n+++ b/src/textual/widgets/_rich_log.py\n@@ -88,9 +88,15 @@\n         \"\"\"Automatically scroll to the end on write.\"\"\"\n         self.highlighter = ReprHighlighter()\n \n+        self._last_container_width: int = min_width\n+        \"\"\"Record the last width we rendered content at.\"\"\"\n+\n     def notify_style_update(self) -> None:\n         self._line_cache.clear()\n \n+    def on_resize(self) -> None:\n+        self._last_container_width = self.scrollable_content_region.width\n+\n     def _make_renderable(self, content: RenderableType | object) -> RenderableType:\n         \"\"\"Make content renderable.\n \n@@ -153,14 +159,22 @@\n         render_width = measure_renderables(\n             console, render_options, [renderable]\n         ).maximum\n+\n         container_width = (\n             self.scrollable_content_region.width if width is None else width\n         )\n-        if container_width:\n-            if expand and render_width < container_width:\n-                render_width = container_width\n-            if shrink and render_width > container_width:\n-                render_width = container_width\n+\n+        # Use the container_width if it's available, otherwise use the last available width.\n+        container_width = (\n+            container_width if container_width else self._last_container_width\n+        )\n+\n+        if expand and render_width < container_width:\n+            render_width = container_width\n+        if shrink and render_width > container_width:\n+            render_width = container_width\n+\n+        render_width = max(render_width, self.min_width)\n \n         segments = self.app.console.render(\n             renderable, render_options.update_width(render_width)\n", "issue": "Writing to a hidden RichLog breaks table alignment\nI've got a `RichLog` that is often hidden and I write text to it. I'm using a `Rich.Table` to left/right justify two pieces of text on a single line.\r\n\r\nThis works as expected while the widget is shown.\r\n\r\nIf the widget has either `visible=False` or `display=False` set, then any text written during that time is collapsed together into a single string.\r\n\r\nI'm not sure if this is a side effect of not having the widget displayed (no space is calculated for it so it doesn't know what sizes to use when writing the `Table`?) or not. If this is working as expected, might there be any way to write to the widget while it's hidden but preserve the appropriate sizing?\r\n\r\nThe following is an example of writing to the widget both while it is shown, has `visible=False` and `display=False`.\r\n\r\n![hidden-width](https://github.com/Textualize/textual/assets/11033703/1cc78911-f193-4186-9b6a-7fc0e0246bf5)\r\n\r\nExample Code (same that produced `gif` above).\r\n```python\r\nfrom __future__ import annotations\r\n\r\nimport random\r\n\r\nfrom rich.align import Align\r\nfrom rich.table import Table\r\nfrom rich.text import Text\r\nfrom textual.app import App, ComposeResult\r\nfrom textual.widgets import RichLog\r\n\r\n\r\nclass Example(App):\r\n    BINDINGS = [\r\n        (\"w\", \"write_line\", \"Write Line\"),\r\n        (\"h\", \"toggle_visible\", \"Toggle Visibility\"),\r\n        (\"d\", \"toggle_display\", \"Toggle Display\"),\r\n        (\"q\", \"quit\", \"Quit\"),\r\n    ]\r\n\r\n    DEFAULT_CSS = \"\"\"\r\n    #one  {\r\n        border: round white;\r\n        height: 1fr;\r\n    }\r\n    #two  {\r\n        border: round white;\r\n        height: 1fr;\r\n    }\r\n    \"\"\"\r\n\r\n    def compose(self) -> ComposeResult:\r\n        log1 = RichLog(id=\"one\")\r\n        log1.border_title = \"Log 1\"\r\n        log2 = RichLog(id=\"two\")\r\n        log2.border_title = \"Log 2\"\r\n        yield log1\r\n        yield log2\r\n\r\n    def action_toggle_visible(self) -> None:\r\n        log1 = self.query_one(\"#one\", RichLog)\r\n        log2 = self.query_one(\"#two\", RichLog)\r\n\r\n        log2.visible = not log2.visible\r\n        log1.write(f\"log2 visibility={log2.visible}\")\r\n\r\n    def action_toggle_display(self) -> None:\r\n        log1 = self.query_one(\"#one\", RichLog)\r\n        log2 = self.query_one(\"#two\", RichLog)\r\n\r\n        log2.display = not log2.display\r\n        log1.write(f\"log2 display={log2.display}\")\r\n\r\n    def action_write_line(self) -> None:\r\n        \"\"\"Write a line to log 2\"\"\"\r\n        log1 = self.query_one(\"#one\", RichLog)\r\n        log2 = self.query_one(\"#two\", RichLog)\r\n\r\n        left = Text(\"something: text on the left\")\r\n        right = Text(\"Stuff on the right\")\r\n        table = Table.grid(expand=True)\r\n        table.add_row(left, Align.right(right))\r\n\r\n        log2.write(table, expand=True)\r\n        log1.write(\"Wrote to log2\")\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    app = Example()\r\n    app.run()\r\n```\r\n\r\n# Textual Diagnostics\r\n\r\n## Versions\r\n\r\n| Name    | Value  |\r\n|---------|--------|\r\n| Textual | 0.46.0 |\r\n| Rich    | 13.7.0 |\r\n\r\n## Python\r\n\r\n| Name           | Value                                                     |\r\n|----------------|-----------------------------------------------------------|\r\n| Version        | 3.8.10                                                    |\r\n| Implementation | CPython                                                   |\r\n| Compiler       | Clang 14.0.3 (clang-1403.0.22.14.1)                       |\r\n| Executable     | /Users/geoff/Documents/projects/kafka-tui/venv/bin/python |\r\n\r\n## Operating System\r\n\r\n| Name    | Value                                                                                                 |\r\n|---------|-------------------------------------------------------------------------------------------------------|\r\n| System  | Darwin                                                                                                |\r\n| Release | 23.3.0                                                                                                |\r\n| Version | Darwin Kernel Version 23.3.0: Wed Dec 20 21:30:27 PST 2023; root:xnu-10002.81.5~7/RELEASE_ARM64_T8103 |\r\n\r\n## Terminal\r\n\r\n| Name                 | Value           |\r\n|----------------------|-----------------|\r\n| Terminal Application | vscode (1.86.0) |\r\n| TERM                 | xterm-256color  |\r\n| COLORTERM            | truecolor       |\r\n| FORCE_COLOR          | *Not set*       |\r\n| NO_COLOR             | *Not set*       |\r\n\r\n## Rich Console options\r\n\r\n| Name           | Value                |\r\n|----------------|----------------------|\r\n| size           | width=115, height=40 |\r\n| legacy_windows | False                |\r\n| min_width      | 1                    |\r\n| max_width      | 115                  |\r\n| is_terminal    | True                 |\r\n| encoding       | utf-8                |\r\n| max_height     | 40                   |\r\n| justify        | None                 |\r\n| overflow       | None                 |\r\n| no_wrap        | False                |\r\n| highlight      | None                 |\r\n| markup         | None                 |\r\n| height         | None                 |\r\n\r\nFeel free to add screenshots and / or videos. These can be very helpful!\r\n\n", "before_files": [{"content": "\"\"\"Provides a scrollable text-logging widget.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Optional, cast\n\nfrom rich.console import RenderableType\nfrom rich.highlighter import ReprHighlighter\nfrom rich.measure import measure_renderables\nfrom rich.pretty import Pretty\nfrom rich.protocol import is_renderable\nfrom rich.segment import Segment\nfrom rich.text import Text\n\nfrom ..cache import LRUCache\nfrom ..geometry import Region, Size\nfrom ..reactive import var\nfrom ..scroll_view import ScrollView\nfrom ..strip import Strip\n\nif TYPE_CHECKING:\n    from typing_extensions import Self\n\n\nclass RichLog(ScrollView, can_focus=True):\n    \"\"\"A widget for logging text.\"\"\"\n\n    DEFAULT_CSS = \"\"\"\n    RichLog{\n        background: $surface;\n        color: $text;\n        overflow-y: scroll;\n    }\n    \"\"\"\n\n    max_lines: var[int | None] = var[Optional[int]](None)\n    min_width: var[int] = var(78)\n    wrap: var[bool] = var(False)\n    highlight: var[bool] = var(False)\n    markup: var[bool] = var(False)\n    auto_scroll: var[bool] = var(True)\n\n    def __init__(\n        self,\n        *,\n        max_lines: int | None = None,\n        min_width: int = 78,\n        wrap: bool = False,\n        highlight: bool = False,\n        markup: bool = False,\n        auto_scroll: bool = True,\n        name: str | None = None,\n        id: str | None = None,\n        classes: str | None = None,\n        disabled: bool = False,\n    ) -> None:\n        \"\"\"Create a RichLog widget.\n\n        Args:\n            max_lines: Maximum number of lines in the log or `None` for no maximum.\n            min_width: Minimum width of renderables.\n            wrap: Enable word wrapping (default is off).\n            highlight: Automatically highlight content.\n            markup: Apply Rich console markup.\n            auto_scroll: Enable automatic scrolling to end.\n            name: The name of the text log.\n            id: The ID of the text log in the DOM.\n            classes: The CSS classes of the text log.\n            disabled: Whether the text log is disabled or not.\n        \"\"\"\n        super().__init__(name=name, id=id, classes=classes, disabled=disabled)\n        self.max_lines = max_lines\n        \"\"\"Maximum number of lines in the log or `None` for no maximum.\"\"\"\n        self._start_line: int = 0\n        self.lines: list[Strip] = []\n        self._line_cache: LRUCache[tuple[int, int, int, int], Strip]\n        self._line_cache = LRUCache(1024)\n        self.max_width: int = 0\n        self.min_width = min_width\n        \"\"\"Minimum width of renderables.\"\"\"\n        self.wrap = wrap\n        \"\"\"Enable word wrapping.\"\"\"\n        self.highlight = highlight\n        \"\"\"Automatically highlight content.\"\"\"\n        self.markup = markup\n        \"\"\"Apply Rich console markup.\"\"\"\n        self.auto_scroll = auto_scroll\n        \"\"\"Automatically scroll to the end on write.\"\"\"\n        self.highlighter = ReprHighlighter()\n\n    def notify_style_update(self) -> None:\n        self._line_cache.clear()\n\n    def _make_renderable(self, content: RenderableType | object) -> RenderableType:\n        \"\"\"Make content renderable.\n\n        Args:\n            content: Content to render.\n\n        Returns:\n            A Rich renderable.\n        \"\"\"\n        renderable: RenderableType\n        if not is_renderable(content):\n            renderable = Pretty(content)\n        else:\n            if isinstance(content, str):\n                if self.markup:\n                    renderable = Text.from_markup(content)\n                else:\n                    renderable = Text(content)\n                if self.highlight:\n                    renderable = self.highlighter(renderable)\n            else:\n                renderable = cast(RenderableType, content)\n\n        if isinstance(renderable, Text):\n            renderable.expand_tabs()\n\n        return renderable\n\n    def write(\n        self,\n        content: RenderableType | object,\n        width: int | None = None,\n        expand: bool = False,\n        shrink: bool = True,\n        scroll_end: bool | None = None,\n    ) -> Self:\n        \"\"\"Write text or a rich renderable.\n\n        Args:\n            content: Rich renderable (or text).\n            width: Width to render or `None` to use optimal width.\n            expand: Enable expand to widget width, or `False` to use `width`.\n            shrink: Enable shrinking of content to fit width.\n            scroll_end: Enable automatic scroll to end, or `None` to use `self.auto_scroll`.\n\n        Returns:\n            The `RichLog` instance.\n        \"\"\"\n\n        auto_scroll = self.auto_scroll if scroll_end is None else scroll_end\n\n        console = self.app.console\n        render_options = console.options\n\n        renderable = self._make_renderable(content)\n\n        if isinstance(renderable, Text) and not self.wrap:\n            render_options = render_options.update(overflow=\"ignore\", no_wrap=True)\n\n        render_width = measure_renderables(\n            console, render_options, [renderable]\n        ).maximum\n        container_width = (\n            self.scrollable_content_region.width if width is None else width\n        )\n        if container_width:\n            if expand and render_width < container_width:\n                render_width = container_width\n            if shrink and render_width > container_width:\n                render_width = container_width\n\n        segments = self.app.console.render(\n            renderable, render_options.update_width(render_width)\n        )\n        lines = list(Segment.split_lines(segments))\n        if not lines:\n            self.lines.append(Strip.blank(render_width))\n        else:\n            self.max_width = max(\n                self.max_width,\n                max(sum([segment.cell_length for segment in _line]) for _line in lines),\n            )\n            strips = Strip.from_lines(lines)\n            for strip in strips:\n                strip.adjust_cell_length(render_width)\n            self.lines.extend(strips)\n\n            if self.max_lines is not None and len(self.lines) > self.max_lines:\n                self._start_line += len(self.lines) - self.max_lines\n                self.refresh()\n                self.lines = self.lines[-self.max_lines :]\n        self.virtual_size = Size(self.max_width, len(self.lines))\n        if auto_scroll:\n            self.scroll_end(animate=False)\n\n        return self\n\n    def clear(self) -> Self:\n        \"\"\"Clear the text log.\n\n        Returns:\n            The `RichLog` instance.\n        \"\"\"\n        self.lines.clear()\n        self._line_cache.clear()\n        self._start_line = 0\n        self.max_width = 0\n        self.virtual_size = Size(self.max_width, len(self.lines))\n        self.refresh()\n        return self\n\n    def render_line(self, y: int) -> Strip:\n        scroll_x, scroll_y = self.scroll_offset\n        line = self._render_line(scroll_y + y, scroll_x, self.size.width)\n        strip = line.apply_style(self.rich_style)\n        return strip\n\n    def render_lines(self, crop: Region) -> list[Strip]:\n        \"\"\"Render the widget in to lines.\n\n        Args:\n            crop: Region within visible area to.\n\n        Returns:\n            A list of list of segments.\n        \"\"\"\n        lines = self._styles_cache.render_widget(self, crop)\n        return lines\n\n    def _render_line(self, y: int, scroll_x: int, width: int) -> Strip:\n        if y >= len(self.lines):\n            return Strip.blank(width, self.rich_style)\n\n        key = (y + self._start_line, scroll_x, width, self.max_width)\n        if key in self._line_cache:\n            return self._line_cache[key]\n\n        line = self.lines[y].crop_extend(scroll_x, scroll_x + width, self.rich_style)\n\n        self._line_cache[key] = line\n        return line\n", "path": "src/textual/widgets/_rich_log.py"}], "after_files": [{"content": "\"\"\"Provides a scrollable text-logging widget.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Optional, cast\n\nfrom rich.console import RenderableType\nfrom rich.highlighter import ReprHighlighter\nfrom rich.measure import measure_renderables\nfrom rich.pretty import Pretty\nfrom rich.protocol import is_renderable\nfrom rich.segment import Segment\nfrom rich.text import Text\n\nfrom ..cache import LRUCache\nfrom ..geometry import Region, Size\nfrom ..reactive import var\nfrom ..scroll_view import ScrollView\nfrom ..strip import Strip\n\nif TYPE_CHECKING:\n    from typing_extensions import Self\n\n\nclass RichLog(ScrollView, can_focus=True):\n    \"\"\"A widget for logging text.\"\"\"\n\n    DEFAULT_CSS = \"\"\"\n    RichLog{\n        background: $surface;\n        color: $text;\n        overflow-y: scroll;\n    }\n    \"\"\"\n\n    max_lines: var[int | None] = var[Optional[int]](None)\n    min_width: var[int] = var(78)\n    wrap: var[bool] = var(False)\n    highlight: var[bool] = var(False)\n    markup: var[bool] = var(False)\n    auto_scroll: var[bool] = var(True)\n\n    def __init__(\n        self,\n        *,\n        max_lines: int | None = None,\n        min_width: int = 78,\n        wrap: bool = False,\n        highlight: bool = False,\n        markup: bool = False,\n        auto_scroll: bool = True,\n        name: str | None = None,\n        id: str | None = None,\n        classes: str | None = None,\n        disabled: bool = False,\n    ) -> None:\n        \"\"\"Create a RichLog widget.\n\n        Args:\n            max_lines: Maximum number of lines in the log or `None` for no maximum.\n            min_width: Minimum width of renderables.\n            wrap: Enable word wrapping (default is off).\n            highlight: Automatically highlight content.\n            markup: Apply Rich console markup.\n            auto_scroll: Enable automatic scrolling to end.\n            name: The name of the text log.\n            id: The ID of the text log in the DOM.\n            classes: The CSS classes of the text log.\n            disabled: Whether the text log is disabled or not.\n        \"\"\"\n        super().__init__(name=name, id=id, classes=classes, disabled=disabled)\n        self.max_lines = max_lines\n        \"\"\"Maximum number of lines in the log or `None` for no maximum.\"\"\"\n        self._start_line: int = 0\n        self.lines: list[Strip] = []\n        self._line_cache: LRUCache[tuple[int, int, int, int], Strip]\n        self._line_cache = LRUCache(1024)\n        self.max_width: int = 0\n        self.min_width = min_width\n        \"\"\"Minimum width of renderables.\"\"\"\n        self.wrap = wrap\n        \"\"\"Enable word wrapping.\"\"\"\n        self.highlight = highlight\n        \"\"\"Automatically highlight content.\"\"\"\n        self.markup = markup\n        \"\"\"Apply Rich console markup.\"\"\"\n        self.auto_scroll = auto_scroll\n        \"\"\"Automatically scroll to the end on write.\"\"\"\n        self.highlighter = ReprHighlighter()\n\n        self._last_container_width: int = min_width\n        \"\"\"Record the last width we rendered content at.\"\"\"\n\n    def notify_style_update(self) -> None:\n        self._line_cache.clear()\n\n    def on_resize(self) -> None:\n        self._last_container_width = self.scrollable_content_region.width\n\n    def _make_renderable(self, content: RenderableType | object) -> RenderableType:\n        \"\"\"Make content renderable.\n\n        Args:\n            content: Content to render.\n\n        Returns:\n            A Rich renderable.\n        \"\"\"\n        renderable: RenderableType\n        if not is_renderable(content):\n            renderable = Pretty(content)\n        else:\n            if isinstance(content, str):\n                if self.markup:\n                    renderable = Text.from_markup(content)\n                else:\n                    renderable = Text(content)\n                if self.highlight:\n                    renderable = self.highlighter(renderable)\n            else:\n                renderable = cast(RenderableType, content)\n\n        if isinstance(renderable, Text):\n            renderable.expand_tabs()\n\n        return renderable\n\n    def write(\n        self,\n        content: RenderableType | object,\n        width: int | None = None,\n        expand: bool = False,\n        shrink: bool = True,\n        scroll_end: bool | None = None,\n    ) -> Self:\n        \"\"\"Write text or a rich renderable.\n\n        Args:\n            content: Rich renderable (or text).\n            width: Width to render or `None` to use optimal width.\n            expand: Enable expand to widget width, or `False` to use `width`.\n            shrink: Enable shrinking of content to fit width.\n            scroll_end: Enable automatic scroll to end, or `None` to use `self.auto_scroll`.\n\n        Returns:\n            The `RichLog` instance.\n        \"\"\"\n\n        auto_scroll = self.auto_scroll if scroll_end is None else scroll_end\n\n        console = self.app.console\n        render_options = console.options\n\n        renderable = self._make_renderable(content)\n\n        if isinstance(renderable, Text) and not self.wrap:\n            render_options = render_options.update(overflow=\"ignore\", no_wrap=True)\n\n        render_width = measure_renderables(\n            console, render_options, [renderable]\n        ).maximum\n\n        container_width = (\n            self.scrollable_content_region.width if width is None else width\n        )\n\n        # Use the container_width if it's available, otherwise use the last available width.\n        container_width = (\n            container_width if container_width else self._last_container_width\n        )\n\n        if expand and render_width < container_width:\n            render_width = container_width\n        if shrink and render_width > container_width:\n            render_width = container_width\n\n        render_width = max(render_width, self.min_width)\n\n        segments = self.app.console.render(\n            renderable, render_options.update_width(render_width)\n        )\n        lines = list(Segment.split_lines(segments))\n        if not lines:\n            self.lines.append(Strip.blank(render_width))\n        else:\n            self.max_width = max(\n                self.max_width,\n                max(sum([segment.cell_length for segment in _line]) for _line in lines),\n            )\n            strips = Strip.from_lines(lines)\n            for strip in strips:\n                strip.adjust_cell_length(render_width)\n            self.lines.extend(strips)\n\n            if self.max_lines is not None and len(self.lines) > self.max_lines:\n                self._start_line += len(self.lines) - self.max_lines\n                self.refresh()\n                self.lines = self.lines[-self.max_lines :]\n        self.virtual_size = Size(self.max_width, len(self.lines))\n        if auto_scroll:\n            self.scroll_end(animate=False)\n\n        return self\n\n    def clear(self) -> Self:\n        \"\"\"Clear the text log.\n\n        Returns:\n            The `RichLog` instance.\n        \"\"\"\n        self.lines.clear()\n        self._line_cache.clear()\n        self._start_line = 0\n        self.max_width = 0\n        self.virtual_size = Size(self.max_width, len(self.lines))\n        self.refresh()\n        return self\n\n    def render_line(self, y: int) -> Strip:\n        scroll_x, scroll_y = self.scroll_offset\n        line = self._render_line(scroll_y + y, scroll_x, self.size.width)\n        strip = line.apply_style(self.rich_style)\n        return strip\n\n    def render_lines(self, crop: Region) -> list[Strip]:\n        \"\"\"Render the widget in to lines.\n\n        Args:\n            crop: Region within visible area to.\n\n        Returns:\n            A list of list of segments.\n        \"\"\"\n        lines = self._styles_cache.render_widget(self, crop)\n        return lines\n\n    def _render_line(self, y: int, scroll_x: int, width: int) -> Strip:\n        if y >= len(self.lines):\n            return Strip.blank(width, self.rich_style)\n\n        key = (y + self._start_line, scroll_x, width, self.max_width)\n        if key in self._line_cache:\n            return self._line_cache[key]\n\n        line = self.lines[y].crop_extend(scroll_x, scroll_x + width, self.rich_style)\n\n        self._line_cache[key] = line\n        return line\n", "path": "src/textual/widgets/_rich_log.py"}]}