Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [8.0.1] - 2026-03-01

### Fixed

- `DirectoryTree` runs more operations in a thread to avoid micro-freezes

### Changes

- Some tweaks to garbage collection to reduce gc time

## [8.0.0] - 2026-02-16

### Added
Expand Down Expand Up @@ -3350,6 +3360,7 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040
- New handler system for messages that doesn't require inheritance
- Improved traceback handling

[8.0.1]: https://github.com/Textualize/textual/compare/v8.0.0...v8.0.1
[8.0.0]: https://github.com/Textualize/textual/compare/v7.5.0...v8.0.0
[7.5.0]: https://github.com/Textualize/textual/compare/v7.4.0...v7.5.0
[7.4.0]: https://github.com/Textualize/textual/compare/v7.3.0...v7.4.0
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "textual"
version = "8.0.0"
version = "8.0.1"
homepage = "https://github.com/Textualize/textual"
repository = "https://github.com/Textualize/textual"
documentation = "https://textual.textualize.io/"
Expand Down
23 changes: 20 additions & 3 deletions src/textual/css/styles.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import weakref
from dataclasses import dataclass, field
from functools import partial
from operator import attrgetter
Expand Down Expand Up @@ -256,8 +257,6 @@ class StylesBase:
"line_pad",
}

node: DOMNode | None = None

display = StringEnumProperty(VALID_DISPLAY, "block", layout=True, display=True)
"""Set the display of the widget, defining how it's rendered.

Expand Down Expand Up @@ -519,6 +518,11 @@ class StylesBase:
Requires terminal support for Kitty pointer shapes protocol.
"""

@property
def node(self) -> DOMNode | None:
"""The DOM node the styles will be applied to, or `None` if it is not set."""
return None

def __textual_animation__(
self,
attribute: str,
Expand Down Expand Up @@ -1331,14 +1335,22 @@ class RenderStyles(StylesBase):
"""Presents a combined view of two Styles object: a base Styles and inline Styles."""

def __init__(self, node: DOMNode, base: Styles, inline_styles: Styles) -> None:
self.node = node
self._node = weakref.ref(node)
self._base_styles = base
self._inline_styles = inline_styles
self._animate: BoundAnimator | None = None
self._updates: int = 0
self._rich_style: tuple[int, Style] | None = None
self._gutter: tuple[int, Spacing] | None = None

def _update_node(self, node: DOMNode) -> None:
"""Update the associated DOM node.

Args:
node: New node for the styles.
"""
self._node = weakref.ref(node)

def __eq__(self, other: object) -> bool:
if isinstance(other, RenderStyles):
return (
Expand All @@ -1356,6 +1368,11 @@ def _cache_key(self) -> int:
"""
return self._updates + self._base_styles._updates + self._inline_styles._updates

@property
def node(self) -> DOMNode | None:
"""The DOM node the styles will be applied to, or `None` if it is not set."""
return self._node()

@property
def base(self) -> Styles:
"""Quick access to base (css) style."""
Expand Down
3 changes: 2 additions & 1 deletion src/textual/dom.py
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,8 @@ def get_component_styles(self, *names: str) -> RenderStyles:
if name not in self._component_styles:
raise KeyError(f"No {name!r} key in COMPONENT_CLASSES")
component_styles = self._component_styles[name]
styles.node = component_styles.node
assert component_styles.node is not None
styles._update_node(component_styles.node)
styles.base.merge(component_styles.base)
styles.inline.merge(component_styles.inline)
styles._updates += 1
Expand Down
10 changes: 9 additions & 1 deletion src/textual/widgets/_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import asyncio
import re
import weakref
from contextlib import suppress
from functools import partial
from pathlib import Path, PurePath
Expand Down Expand Up @@ -210,7 +211,7 @@ def __init__(
*args,
**kwargs,
) -> None:
self._markdown: Markdown = markdown
self._markdown_ref = weakref.ref(markdown)
"""A reference to the Markdown document that contains this block."""
self._content: Content = Content()
self._token: Token = token
Expand All @@ -224,6 +225,13 @@ def __init__(
*args, name=token.type, classes=f"level-{token.level}", **kwargs
)

@property
def _markdown(self) -> Markdown:
"""Resolve the weak ref to _markdown"""
markdown = self._markdown_ref()
assert markdown is not None
return markdown

@property
def select_container(self) -> Widget:
return self.query_ancestor(Markdown)
Expand Down
2 changes: 1 addition & 1 deletion tests/css/test_styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def test_merge_rules():
def test_render_styles_border():
base = Styles()
inline = Styles()
styles_view = RenderStyles(None, base, inline)
styles_view = RenderStyles(DOMNode(), base, inline)

base.border_top = ("heavy", "red")
# Base has border-top: heavy red
Expand Down
Loading