diff --git a/CHANGELOG.md b/CHANGELOG.md index b3969b4968..a497de7b6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Optimized startup https://github.com/Textualize/textual/pull/5869 - New blank visual which makes background faster to render (note this will break snapshots tests this version) https://github.com/Textualize/textual/pull/5869 +- Exposed `code_indent_guides` boolean on Markdown widget https://github.com/Textualize/textual/pull/5874 +- Changed code fence background to use CSS background rather than its code theme https://github.com/Textualize/textual/pull/5874 ## [3.4.0] - 2025-06-14 diff --git a/Makefile b/Makefile index c7e2e74fd6..fd35774d27 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ docs-online-nav: .PHONY: docs-serve docs-serve: clean-screenshot-cache docs-online-nav - $(run) mkdocs serve --config-file mkdocs-nav-online.yml + TEXTUAL_THEME=dracula $(run) mkdocs serve --config-file mkdocs-nav-online.yml rm -f mkdocs-nav-online.yml .PHONY: docs-serve-offline @@ -76,7 +76,7 @@ clean-offline-docs: .PHONY: docs-deploy docs-deploy: clean-screenshot-cache docs-online-nav - $(run) mkdocs gh-deploy --config-file mkdocs-nav-online.yml + TEXTUAL_THEME=dracula $(run) mkdocs gh-deploy --config-file mkdocs-nav-online.yml rm -f mkdocs-nav-online.yml .PHONY: build diff --git a/docs/examples/widgets/markdown.py b/docs/examples/widgets/markdown.py index 1294b53f6b..c51101d85a 100644 --- a/docs/examples/widgets/markdown.py +++ b/docs/examples/widgets/markdown.py @@ -2,25 +2,58 @@ from textual.widgets import Markdown EXAMPLE_MARKDOWN = """\ -# Markdown Document +## Markdown -This is an example of Textual's `Markdown` widget. - -## Features - -Markdown syntax and extensions are supported. - -- Typography *emphasis*, **strong**, `inline code` etc. -- Headers -- Lists (bullet and ordered) +- Typography *emphasis*, **strong**, `inline code` etc. +- Headers +- Lists - Syntax highlighted code blocks -- Tables! +- Tables and more + +## Quotes + +> I must not fear. +> > Fear is the mind-killer. +> > Fear is the little-death that brings total obliteration. +> > I will face my fear. +> > > I will permit it to pass over me and through me. +> > > And when it has gone past, I will turn the inner eye to see its path. +> > > Where the fear has gone there will be nothing. Only I will remain. + +## Tables + +| Name | Type | Default | Description | +| --------------- | ------ | ------- | ---------------------------------- | +| `show_header` | `bool` | `True` | Show the table header | +| `fixed_rows` | `int` | `0` | Number of fixed rows | +| `fixed_columns` | `int` | `0` | Number of fixed columns | + +## Code blocks + +```python +def loop_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]: + \"\"\"Iterate and generate a tuple with a flag for last value.\"\"\" + iter_values = iter(values) + try: + previous_value = next(iter_values) + except StopIteration: + return + for value in iter_values: + yield False, previous_value + previous_value = value + yield True, previous_value +``` + + """ class MarkdownExampleApp(App): + def compose(self) -> ComposeResult: - yield Markdown(EXAMPLE_MARKDOWN) + markdown = Markdown(EXAMPLE_MARKDOWN) + markdown.code_indent_guides = False + yield markdown if __name__ == "__main__": diff --git a/docs/examples/widgets/markdown_viewer.py b/docs/examples/widgets/markdown_viewer.py index 13843905a6..e1bc99604c 100644 --- a/docs/examples/widgets/markdown_viewer.py +++ b/docs/examples/widgets/markdown_viewer.py @@ -33,7 +33,7 @@ ## Code Blocks -Code blocks are syntax highlighted, with guidelines. +Code blocks are syntax highlighted. ```python class ListViewExample(App): @@ -45,12 +45,24 @@ def compose(self) -> ComposeResult: ) yield Footer() ``` + +## Litany Against Fear + +I must not fear. +Fear is the mind-killer. +Fear is the little-death that brings total obliteration. +I will face my fear. +I will permit it to pass over me and through me. +And when it has gone past, I will turn the inner eye to see its path. +Where the fear has gone there will be nothing. Only I will remain. """ class MarkdownExampleApp(App): def compose(self) -> ComposeResult: - yield MarkdownViewer(EXAMPLE_MARKDOWN, show_table_of_contents=True) + markdown_viewer = MarkdownViewer(EXAMPLE_MARKDOWN, show_table_of_contents=True) + markdown_viewer.code_indent_guides = False + yield markdown_viewer if __name__ == "__main__": diff --git a/docs/widget_gallery.md b/docs/widget_gallery.md index f1d26d69fa..d42f065383 100644 --- a/docs/widget_gallery.md +++ b/docs/widget_gallery.md @@ -162,7 +162,7 @@ Display and interact with a Markdown document (adds a table of contents and brow [MarkdownViewer reference](./widgets/markdown_viewer.md){ .md-button .md-button--primary } -```{.textual path="docs/examples/widgets/markdown_viewer.py" columns="100" lines="42"} +```{.textual path="docs/examples/widgets/markdown_viewer.py" columns="120" lines="50" press="tab,down"} ``` ## Markdown @@ -172,7 +172,7 @@ Display a markdown document. [Markdown reference](./widgets/markdown.md){ .md-button .md-button--primary } -```{.textual path="docs/examples/widgets/markdown.py"} +```{.textual path="docs/examples/widgets/markdown.py" columns="120" lines="51"} ``` ## MaskedInput diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py index ad796b173f..fdc32c0144 100644 --- a/src/textual/widgets/_markdown.py +++ b/src/textual/widgets/_markdown.py @@ -352,7 +352,7 @@ class MarkdownBlockQuote(MarkdownBlock): DEFAULT_CSS = """ MarkdownBlockQuote { background: $boost; - border-left: outer $success-darken-2; + border-left: outer $primary 50%; margin: 1 0; padding: 0 1; } @@ -478,7 +478,7 @@ def __init__(self, headers: list[Text], rows: list[list[Text]]): def render(self) -> Table: table = Table( expand=True, - box=box.SIMPLE_HEAVY, + box=box.SIMPLE_HEAD, style=self.rich_style, header_style=self.get_component_rich_style("markdown-table--header"), border_style=self.get_component_rich_style("markdown-table--lines"), @@ -504,7 +504,10 @@ class MarkdownTable(MarkdownBlock): DEFAULT_CSS = """ MarkdownTable { width: 100%; - background: $surface; + background: black 10%; + &:light { + background: white 30%; + } } """ @@ -555,7 +558,7 @@ class MarkdownBullet(Widget): DEFAULT_CSS = """ MarkdownBullet { width: auto; - color: $success; + color: $text; text-style: bold; &:light { color: $secondary; @@ -613,6 +616,11 @@ class MarkdownFence(MarkdownBlock): height: auto; max-height: 20; color: rgb(210,210,210); + background: black 10%; + + &:light { + background: white 30%; + } } MarkdownFence > * { @@ -630,14 +638,19 @@ def __init__(self, markdown: Markdown, code: str, lexer: str) -> None: else self._markdown.code_light_theme ) + def notify_style_update(self) -> None: + self.call_later(self._retheme) + def _block(self) -> Syntax: + _, background_color = self.background_colors return Syntax( self.code, lexer=self.lexer, word_wrap=False, - indent_guides=True, + indent_guides=self._markdown.code_indent_guides, padding=(1, 2), theme=self.theme, + background_color=background_color.css, ) def _on_mount(self, _: Mount) -> None: @@ -722,6 +735,9 @@ class Markdown(Widget): code_light_theme: reactive[str] = reactive("material-light") """The theme to use for code blocks when the App theme is light.""" + code_indent_guides: reactive[bool] = reactive(True) + """Should code fences display indent guides?""" + def __init__( self, markdown: str | None = None, @@ -1157,6 +1173,9 @@ class MarkdownViewer(VerticalScroll, can_focus=False, can_focus_children=True): """ show_table_of_contents = reactive(True) + """Show the table of contents?""" + code_indent_guides: reactive[bool] = reactive(True) + """Should code fences display indent guides?""" top_block = reactive("") navigator: var[Navigator] = var(Navigator) @@ -1241,7 +1260,7 @@ def compose(self) -> ComposeResult: parser_factory=self._parser_factory, open_links=self._open_links ) markdown.can_focus = True - yield markdown + yield markdown.data_bind(MarkdownViewer.code_indent_guides) yield MarkdownTableOfContents(markdown) def _on_markdown_table_of_contents_updated( diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_content_switcher_example_switch.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_content_switcher_example_switch.svg index dcab07e1e2..2c52f7cb6d 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_content_switcher_example_switch.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_content_switcher_example_switch.svg @@ -204,7 +204,7 @@ - + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ @@ -226,7 +226,7 @@ UK Release   Flavour   Date        Director    - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  + ───────────────────────────────────   Strawberry 2004-04-09   Edgar                                 Wright       @@ -237,7 +237,7 @@ UK Release    Flavour Date         Director     - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  + ───────────────────────────────────   Classico 2007-02-17    Edgar Wright  @@ -247,7 +247,7 @@ UK Release     FlavourDate          Director     - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  + ───────────────────────────────────   Mint    2013-07-19     Edgar Wright  diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_component_classes_reloading.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_component_classes_reloading.svg index 9d8270f46b..405bbd86f1 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_component_classes_reloading.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_component_classes_reloading.svg @@ -128,7 +128,7 @@ - + @@ -136,7 +136,7 @@ col1                                 col2                                 - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  + ──────────────────────────────────────────────────────────────────────────   value 1                               value 2                               Here's some code: from itertools import productBold textEmphasized text diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_dark_theme_override.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_dark_theme_override.svg index 9ffbf5034e..2a14ce6429 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_dark_theme_override.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_dark_theme_override.svg @@ -38,7 +38,7 @@ .terminal-r4 { fill: #859900 } .terminal-r5 { fill: #839496 } .terminal-r6 { fill: #268bd2 } -.terminal-r7 { fill: #445257;font-style: italic; } +.terminal-r7 { fill: #435156;font-style: italic; } .terminal-r8 { fill: #2aa198 } @@ -125,7 +125,7 @@ - + diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_example.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_example.svg index b995b285ca..a8663c5520 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_example.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_example.svg @@ -33,13 +33,17 @@ } .terminal-r1 { fill: #c5c8c6 } -.terminal-r2 { fill: #0178d4;font-weight: bold } -.terminal-r3 { fill: #e0e0e0 } -.terminal-r4 { fill: #9e9e9e;font-weight: bold } -.terminal-r5 { fill: #0178d4;text-decoration: underline; } -.terminal-r6 { fill: #4ebf71;font-weight: bold } -.terminal-r7 { fill: #e0e0e0;font-style: italic; } -.terminal-r8 { fill: #e0e0e0;font-weight: bold } +.terminal-r2 { fill: #121212 } +.terminal-r3 { fill: #0178d4;text-decoration: underline; } +.terminal-r4 { fill: #e1e1e1;font-weight: bold } +.terminal-r5 { fill: #e0e0e0 } +.terminal-r6 { fill: #e0e0e0;font-style: italic; } +.terminal-r7 { fill: #e0e0e0;font-weight: bold } +.terminal-r8 { fill: #9e9e9e;font-weight: bold } +.terminal-r9 { fill: #000000 } +.terminal-r10 { fill: #0f4b79 } +.terminal-r11 { fill: #134f7d } +.terminal-r12 { fill: #175381 } @@ -125,31 +129,31 @@ - + -Markdown Document +Markdown -This is an example of Textual's Markdown widget. - - -Features - -Markdown syntax and extensions are supported. - -● Typography emphasisstronginline code etc. -● Headers -● Lists (bullet and ordered) -● Syntax highlighted code blocks -● Tables! - - - - - - - +● Typography emphasisstronginline code etc. +● Headers +● Lists +● Syntax highlighted code blocks +● Tables and more + +▁▁ +Quotes + +I must not fear. + +Fear is the mind-killer. Fear is the little-death that brings total +obliteration. I will face my fear. + +I will permit it to pass over me and through me. And when it has +gone past, I will turn the inner eye to see its path. Where the +fear has gone there will be nothing. Only I will remain. + + diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_light_theme_override.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_light_theme_override.svg index ef77d30e29..02d9405d4c 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_light_theme_override.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_light_theme_override.svg @@ -38,7 +38,7 @@ .terminal-r4 { fill: #859900 } .terminal-r5 { fill: #657b83 } .terminal-r6 { fill: #268bd2 } -.terminal-r7 { fill: #aab3b3;font-style: italic; } +.terminal-r7 { fill: #aeb7b7;font-style: italic; } .terminal-r8 { fill: #2aa198 } @@ -125,7 +125,7 @@ - + diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_space_squashing.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_space_squashing.svg index cabb86a6cb..78d342448b 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_space_squashing.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_space_squashing.svg @@ -47,7 +47,7 @@ .terminal-r13 { fill: #eeffff } .terminal-r14 { fill: #ffcb6b } .terminal-r15 { fill: #89ddff } -.terminal-r16 { fill: #41525a;font-style: italic; } +.terminal-r16 { fill: #405159;font-style: italic; } .terminal-r17 { fill: #f78c6c } @@ -134,7 +134,7 @@ - + X XX XX X X X X X diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_theme_switching.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_theme_switching.svg index 1fa6b1ad81..6967488ec3 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_theme_switching.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_theme_switching.svg @@ -39,7 +39,7 @@ .terminal-r5 { fill: #bbbbbb } .terminal-r6 { fill: #0000ff } .terminal-r7 { fill: #000000 } -.terminal-r8 { fill: #719a9a;font-style: italic; } +.terminal-r8 { fill: #759e9e;font-style: italic; } .terminal-r9 { fill: #008000 } .terminal-r10 { fill: #ba2121 } @@ -127,7 +127,7 @@ - + diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_viewer_example.svg b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_viewer_example.svg index f5645b1c26..3f862179f2 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_viewer_example.svg +++ b/tests/snapshot_tests/__snapshots__/test_snapshots/test_markdown_viewer_example.svg @@ -41,7 +41,7 @@ .terminal-r7 { fill: #a1a1a1;font-weight: bold } .terminal-r8 { fill: #0178d4;text-decoration: underline; } .terminal-r9 { fill: #000000 } -.terminal-r10 { fill: #4ebf71;font-weight: bold } +.terminal-r10 { fill: #e2e2e2;font-weight: bold } .terminal-r11 { fill: #e0e0e0;font-style: italic; } .terminal-r12 { fill: #e0e0e0;font-weight: bold } @@ -129,30 +129,30 @@ - + ▼ Ⅰ Markdown Viewer -├── Ⅱ FeaturesMarkdown Viewer +├── Ⅱ FeaturesMarkdown Viewer ├── Ⅱ Tables -└── Ⅱ Code BlocksThis is an example of Textual's MarkdownViewer -widget. +├── Ⅱ Code BlocksThis is an example of Textual's MarkdownViewer +└── Ⅱ Litany Against Fearwidget. -Features +Features▄▄ -Markdown syntax and extensions are supported. -▇▇ -● Typography emphasisstronginline code etc. -● Headers -● Lists (bullet and ordered) -● Syntax highlighted code blocks -● Tables! +Markdown syntax and extensions are supported. + +● Typography emphasisstronginline code etc. +● Headers +● Lists (bullet and ordered) +● Syntax highlighted code blocks +● Tables! -Tables +Tables -Tables are displayed in a DataTable widget. +Tables are displayed in a DataTable widget.