Skip to content

Commit 78c286f

Browse files
authored
Merge pull request #5874 from Textualize/markdown-code-guides
Expose code intend guides
2 parents 8f85ece + faef858 commit 78c286f

14 files changed

+151
-81
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1111

1212
- Optimized startup https://github.com/Textualize/textual/pull/5869
1313
- New blank visual which makes background faster to render (note this will break snapshots tests this version) https://github.com/Textualize/textual/pull/5869
14+
- Exposed `code_indent_guides` boolean on Markdown widget https://github.com/Textualize/textual/pull/5874
15+
- Changed code fence background to use CSS background rather than its code theme https://github.com/Textualize/textual/pull/5874
1416

1517
## [3.4.0] - 2025-06-14
1618

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ docs-online-nav:
5252

5353
.PHONY: docs-serve
5454
docs-serve: clean-screenshot-cache docs-online-nav
55-
$(run) mkdocs serve --config-file mkdocs-nav-online.yml
55+
TEXTUAL_THEME=dracula $(run) mkdocs serve --config-file mkdocs-nav-online.yml
5656
rm -f mkdocs-nav-online.yml
5757

5858
.PHONY: docs-serve-offline
@@ -76,7 +76,7 @@ clean-offline-docs:
7676

7777
.PHONY: docs-deploy
7878
docs-deploy: clean-screenshot-cache docs-online-nav
79-
$(run) mkdocs gh-deploy --config-file mkdocs-nav-online.yml
79+
TEXTUAL_THEME=dracula $(run) mkdocs gh-deploy --config-file mkdocs-nav-online.yml
8080
rm -f mkdocs-nav-online.yml
8181

8282
.PHONY: build

docs/examples/widgets/markdown.py

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,58 @@
22
from textual.widgets import Markdown
33

44
EXAMPLE_MARKDOWN = """\
5-
# Markdown Document
5+
## Markdown
66
7-
This is an example of Textual's `Markdown` widget.
8-
9-
## Features
10-
11-
Markdown syntax and extensions are supported.
12-
13-
- Typography *emphasis*, **strong**, `inline code` etc.
14-
- Headers
15-
- Lists (bullet and ordered)
7+
- Typography *emphasis*, **strong**, `inline code` etc.
8+
- Headers
9+
- Lists
1610
- Syntax highlighted code blocks
17-
- Tables!
11+
- Tables and more
12+
13+
## Quotes
14+
15+
> I must not fear.
16+
> > Fear is the mind-killer.
17+
> > Fear is the little-death that brings total obliteration.
18+
> > I will face my fear.
19+
> > > I will permit it to pass over me and through me.
20+
> > > And when it has gone past, I will turn the inner eye to see its path.
21+
> > > Where the fear has gone there will be nothing. Only I will remain.
22+
23+
## Tables
24+
25+
| Name | Type | Default | Description |
26+
| --------------- | ------ | ------- | ---------------------------------- |
27+
| `show_header` | `bool` | `True` | Show the table header |
28+
| `fixed_rows` | `int` | `0` | Number of fixed rows |
29+
| `fixed_columns` | `int` | `0` | Number of fixed columns |
30+
31+
## Code blocks
32+
33+
```python
34+
def loop_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:
35+
\"\"\"Iterate and generate a tuple with a flag for last value.\"\"\"
36+
iter_values = iter(values)
37+
try:
38+
previous_value = next(iter_values)
39+
except StopIteration:
40+
return
41+
for value in iter_values:
42+
yield False, previous_value
43+
previous_value = value
44+
yield True, previous_value
45+
```
46+
47+
1848
"""
1949

2050

2151
class MarkdownExampleApp(App):
52+
2253
def compose(self) -> ComposeResult:
23-
yield Markdown(EXAMPLE_MARKDOWN)
54+
markdown = Markdown(EXAMPLE_MARKDOWN)
55+
markdown.code_indent_guides = False
56+
yield markdown
2457

2558

2659
if __name__ == "__main__":

docs/examples/widgets/markdown_viewer.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
3434
## Code Blocks
3535
36-
Code blocks are syntax highlighted, with guidelines.
36+
Code blocks are syntax highlighted.
3737
3838
```python
3939
class ListViewExample(App):
@@ -45,12 +45,24 @@ def compose(self) -> ComposeResult:
4545
)
4646
yield Footer()
4747
```
48+
49+
## Litany Against Fear
50+
51+
I must not fear.
52+
Fear is the mind-killer.
53+
Fear is the little-death that brings total obliteration.
54+
I will face my fear.
55+
I will permit it to pass over me and through me.
56+
And when it has gone past, I will turn the inner eye to see its path.
57+
Where the fear has gone there will be nothing. Only I will remain.
4858
"""
4959

5060

5161
class MarkdownExampleApp(App):
5262
def compose(self) -> ComposeResult:
53-
yield MarkdownViewer(EXAMPLE_MARKDOWN, show_table_of_contents=True)
63+
markdown_viewer = MarkdownViewer(EXAMPLE_MARKDOWN, show_table_of_contents=True)
64+
markdown_viewer.code_indent_guides = False
65+
yield markdown_viewer
5466

5567

5668
if __name__ == "__main__":

docs/widget_gallery.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ Display and interact with a Markdown document (adds a table of contents and brow
162162
[MarkdownViewer reference](./widgets/markdown_viewer.md){ .md-button .md-button--primary }
163163

164164

165-
```{.textual path="docs/examples/widgets/markdown_viewer.py" columns="100" lines="42"}
165+
```{.textual path="docs/examples/widgets/markdown_viewer.py" columns="120" lines="50" press="tab,down"}
166166
```
167167

168168
## Markdown
@@ -172,7 +172,7 @@ Display a markdown document.
172172
[Markdown reference](./widgets/markdown.md){ .md-button .md-button--primary }
173173

174174

175-
```{.textual path="docs/examples/widgets/markdown.py"}
175+
```{.textual path="docs/examples/widgets/markdown.py" columns="120" lines="51"}
176176
```
177177

178178
## MaskedInput

src/textual/widgets/_markdown.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ class MarkdownBlockQuote(MarkdownBlock):
352352
DEFAULT_CSS = """
353353
MarkdownBlockQuote {
354354
background: $boost;
355-
border-left: outer $success-darken-2;
355+
border-left: outer $primary 50%;
356356
margin: 1 0;
357357
padding: 0 1;
358358
}
@@ -478,7 +478,7 @@ def __init__(self, headers: list[Text], rows: list[list[Text]]):
478478
def render(self) -> Table:
479479
table = Table(
480480
expand=True,
481-
box=box.SIMPLE_HEAVY,
481+
box=box.SIMPLE_HEAD,
482482
style=self.rich_style,
483483
header_style=self.get_component_rich_style("markdown-table--header"),
484484
border_style=self.get_component_rich_style("markdown-table--lines"),
@@ -504,7 +504,10 @@ class MarkdownTable(MarkdownBlock):
504504
DEFAULT_CSS = """
505505
MarkdownTable {
506506
width: 100%;
507-
background: $surface;
507+
background: black 10%;
508+
&:light {
509+
background: white 30%;
510+
}
508511
}
509512
"""
510513

@@ -555,7 +558,7 @@ class MarkdownBullet(Widget):
555558
DEFAULT_CSS = """
556559
MarkdownBullet {
557560
width: auto;
558-
color: $success;
561+
color: $text;
559562
text-style: bold;
560563
&:light {
561564
color: $secondary;
@@ -613,6 +616,11 @@ class MarkdownFence(MarkdownBlock):
613616
height: auto;
614617
max-height: 20;
615618
color: rgb(210,210,210);
619+
background: black 10%;
620+
621+
&:light {
622+
background: white 30%;
623+
}
616624
}
617625
618626
MarkdownFence > * {
@@ -630,14 +638,19 @@ def __init__(self, markdown: Markdown, code: str, lexer: str) -> None:
630638
else self._markdown.code_light_theme
631639
)
632640

641+
def notify_style_update(self) -> None:
642+
self.call_later(self._retheme)
643+
633644
def _block(self) -> Syntax:
645+
_, background_color = self.background_colors
634646
return Syntax(
635647
self.code,
636648
lexer=self.lexer,
637649
word_wrap=False,
638-
indent_guides=True,
650+
indent_guides=self._markdown.code_indent_guides,
639651
padding=(1, 2),
640652
theme=self.theme,
653+
background_color=background_color.css,
641654
)
642655

643656
def _on_mount(self, _: Mount) -> None:
@@ -722,6 +735,9 @@ class Markdown(Widget):
722735
code_light_theme: reactive[str] = reactive("material-light")
723736
"""The theme to use for code blocks when the App theme is light."""
724737

738+
code_indent_guides: reactive[bool] = reactive(True)
739+
"""Should code fences display indent guides?"""
740+
725741
def __init__(
726742
self,
727743
markdown: str | None = None,
@@ -1157,6 +1173,9 @@ class MarkdownViewer(VerticalScroll, can_focus=False, can_focus_children=True):
11571173
"""
11581174

11591175
show_table_of_contents = reactive(True)
1176+
"""Show the table of contents?"""
1177+
code_indent_guides: reactive[bool] = reactive(True)
1178+
"""Should code fences display indent guides?"""
11601179
top_block = reactive("")
11611180

11621181
navigator: var[Navigator] = var(Navigator)
@@ -1241,7 +1260,7 @@ def compose(self) -> ComposeResult:
12411260
parser_factory=self._parser_factory, open_links=self._open_links
12421261
)
12431262
markdown.can_focus = True
1244-
yield markdown
1263+
yield markdown.data_bind(MarkdownViewer.code_indent_guides)
12451264
yield MarkdownTableOfContents(markdown)
12461265

12471266
def _on_markdown_table_of_contents_updated(

tests/snapshot_tests/__snapshots__/test_snapshots/test_content_switcher_example_switch.svg

Lines changed: 4 additions & 4 deletions
Loading

0 commit comments

Comments
 (0)