diff --git a/CHANGELOG.md b/CHANGELOG.md index f2b5b59ae7..0103d65c05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,19 +5,21 @@ 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/). -## Unreleased +## [7.3.0] - 2026-01-15 -### Changed +### Fixed + +- Fixed triple click on command palette raising an exception https://github.com/Textualize/textual/pull/6329 -- Allow `Sparkline` to be of any height, not just 1 https://github.com/Textualize/textual/pull/6171 -- ### Added - Added `DOM.query_one_optional` +- Added `default` parameter to `get_component_rich_style` get_component_rich_style ### Changed - Added super+c (command on mac) alternative bindings for copy, for terminals that support it (Ghostty does) +- Allow `Sparkline` to be of any height, not just 1 https://github.com/Textualize/textual/pull/6171 ## [7.2.0] - 2026-01-11 @@ -3320,6 +3322,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 +[7.3.0]: https://github.com/Textualize/textual/compare/v7.2.0...v7.3.0 [7.2.0]: https://github.com/Textualize/textual/compare/v7.1.0...v7.2.0 [7.1.0]: https://github.com/Textualize/textual/compare/v7.0.3...v7.1.0 [7.0.3]: https://github.com/Textualize/textual/compare/v7.0.2...v7.0.3 diff --git a/pyproject.toml b/pyproject.toml index 05c77ee3a4..0450797920 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "textual" -version = "7.2.0" +version = "7.3.0" homepage = "https://github.com/Textualize/textual" repository = "https://github.com/Textualize/textual" documentation = "https://textual.textualize.io/" diff --git a/src/textual/command.py b/src/textual/command.py index c0d3c415ff..35d59dc84b 100644 --- a/src/textual/command.py +++ b/src/textual/command.py @@ -524,7 +524,7 @@ class CommandPalette(SystemModalScreen[None]): AUTO_FOCUS = "CommandInput" - COMPONENT_CLASSES: ClassVar[set[str]] = { + COMPONENT_CLASSES: ClassVar[set[str]] = Screen.COMPONENT_CLASSES | { "command-palette--help-text", "command-palette--highlight", } diff --git a/src/textual/dom.py b/src/textual/dom.py index fa36ce767c..1687f45abc 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -25,6 +25,7 @@ import rich.repr from rich.highlighter import ReprHighlighter +from rich.style import NULL_STYLE as RICH_NULL_STYLE from rich.style import Style from rich.text import Text from rich.tree import Tree @@ -1072,7 +1073,9 @@ def text_style(self) -> Style: @property def selection_style(self) -> Style: """The style of selected text.""" - style = self.screen.get_component_rich_style("screen--selection") + style = self.screen.get_component_rich_style( + "screen--selection", default=RICH_NULL_STYLE + ) return style @property diff --git a/src/textual/visual.py b/src/textual/visual.py index 4f9f6ea5d7..31ee84b927 100644 --- a/src/textual/visual.py +++ b/src/textual/visual.py @@ -10,6 +10,7 @@ from rich.measure import Measurement from rich.protocol import is_renderable, rich_cast from rich.segment import Segment +from rich.style import NULL_STYLE as RICH_NULL_STYLE from rich.style import Style as RichStyle from rich.text import Text @@ -219,7 +220,9 @@ def to_strips( selection = widget.text_selection if selection is not None: selection_style: Style | None = Style.from_rich_style( - widget.screen.get_component_rich_style("screen--selection") + widget.screen.get_component_rich_style( + "screen--selection", default=RICH_NULL_STYLE + ) ) else: selection_style = None diff --git a/src/textual/widget.py b/src/textual/widget.py index 3efcbb5648..8f577b2b2e 100644 --- a/src/textual/widget.py +++ b/src/textual/widget.py @@ -1140,19 +1140,32 @@ def get_child_by_type(self, expect_type: type[ExpectType]) -> ExpectType: return child raise NoMatches(f"No immediate child of type {expect_type}; {self._nodes}") - def get_component_rich_style(self, *names: str, partial: bool = False) -> Style: + def get_component_rich_style( + self, *names: str, partial: bool = False, default: Style | None = None + ) -> Style: """Get a *Rich* style for a component. Args: names: Names of components. partial: Return a partial style (not combined with parent). + default: A Style to return if any component style doesn't exist. + + Raises: + KeyError: If a component style doesn't exist, and no `default` is provided. Returns: A Rich style object. """ if names not in self._rich_style_cache: - component_styles = self.get_component_styles(*names) + if default is None: + component_styles = self.get_component_styles(*names) + else: + try: + component_styles = self.get_component_styles(*names) + except KeyError: + return default + style = component_styles.rich_style text_opacity = component_styles.text_opacity if text_opacity < 1 and style.bgcolor is not None: