Skip to content
Open
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ 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

### Fixed

- Fixed `MarkupError` crash when hovering a Markdown table cell whose text contains bracketed content, when the table was rendered in one shot rather than streamed https://github.com/Textualize/textual/issues/6642

## [8.2.8] - 2026-06-30

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion src/textual/widgets/_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ def compose(self) -> ComposeResult:
cell,
classes=f"row{row_index} cell",
name=f"cell{row_index}.{cell_index}",
).with_tooltip(cell.plain)
).with_tooltip(cell)
self.last_row = row_index

def _update_content(self, headers: list[Content], rows: list[list[Content]]):
Expand Down
41 changes: 41 additions & 0 deletions tests/test_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,44 @@ def on_markdown_link_clicked(self, message: Markdown.LinkClicked):
async with app.run_test() as pilot:
await pilot.click(Markdown, offset=(3, 0))
assert links == ["tété"]


async def test_markdown_table_cell_tooltip_with_bracketed_text_does_not_crash():
"""A table cell whose text looks like markup (e.g. contains `[...]`)
should not crash when its tooltip is rendered.

The one-shot `compose()` path used to set the tooltip from `cell.plain`
(a plain `str`), which is markup-enabled when rendered. Cell text such as
`[sys:LOCK=SAFE...]` was then mis-parsed as markup and raised
`MarkupError`. The streaming `_update_rows()` path already used the
`Content` object directly (never markup-parsed), which does not crash.

Regression test for https://github.com/Textualize/textual/issues/6642
"""
from textual.visual import visualize
from textual.widgets._markdown import MarkdownTableCellContents

markdown_table = (
"| session | note |\n"
"| --- | --- |\n"
"| aaa-bbb-ccc | first row padding to force truncation |\n"
"| 019f314b-d61d-7837-a158-8aac342ae56f "
"| ([sys:LOCK=SAFE…]) persona override |\n"
)

class MarkdownTableApp(App):
def compose(self) -> ComposeResult:
yield Markdown(markdown_table)

app = MarkdownTableApp()
async with app.run_test() as pilot:
await pilot.pause()
cell_widgets = list(app.query(MarkdownTableCellContents))
assert cell_widgets
for cell_widget in cell_widgets:
tooltip = cell_widget.tooltip
if tooltip is None:
continue
# Rendering the tooltip with markup enabled must not raise
# MarkupError, regardless of what characters the cell contains.
visualize(cell_widget, tooltip, markup=True)