Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Textual markup #5485

Draft
wants to merge 56 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
8b1c2ad
Textual markup
willmcgugan Jan 14, 2025
d9f8125
parse styles
willmcgugan Jan 14, 2025
d657faf
parse syntax
willmcgugan Jan 14, 2025
d6e085c
parse syntax
willmcgugan Jan 14, 2025
aa319af
simplify color output
willmcgugan Jan 14, 2025
c9102ae
switch to Content
willmcgugan Jan 16, 2025
32a2624
markup property
willmcgugan Jan 17, 2025
6d3d64d
Border content
willmcgugan Jan 18, 2025
a745cda
textual style
willmcgugan Jan 18, 2025
b055673
test fix, fix outline
willmcgugan Jan 18, 2025
57525e4
content
willmcgugan Jan 19, 2025
1d5b1cb
docs and tidy
willmcgugan Jan 21, 2025
ceceda7
simplify Content
willmcgugan Jan 21, 2025
83daf3b
tests
willmcgugan Jan 21, 2025
6ec9531
tests of total_ordering
willmcgugan Jan 21, 2025
3f29339
more tests
willmcgugan Jan 21, 2025
d4543ff
tests
willmcgugan Jan 22, 2025
35aaec4
interface fixes
willmcgugan Jan 22, 2025
48815bf
fix test
willmcgugan Jan 22, 2025
284ced4
docstrings
willmcgugan Jan 22, 2025
ed79ffd
fix percentages
willmcgugan Jan 22, 2025
2197fd0
optimize style parse
willmcgugan Jan 23, 2025
79734ee
fix for content
willmcgugan Jan 24, 2025
04dbf88
traceback handling
willmcgugan Jan 24, 2025
aa159dd
Visual protocol
willmcgugan Jan 25, 2025
02f8e0d
textual markup app
willmcgugan Jan 25, 2025
9f93a35
no wrap
willmcgugan Jan 26, 2025
8224ac8
wrap and overflow
willmcgugan Jan 26, 2025
eb8a6ac
back to rulesmap
willmcgugan Jan 26, 2025
4c59821
Merge branch 'main' into textual-markup
willmcgugan Jan 26, 2025
c2f4c2a
overflow test
willmcgugan Jan 26, 2025
a0910ce
wrap and overflow docs
willmcgugan Jan 27, 2025
d44788a
docs
willmcgugan Jan 27, 2025
f0cfabb
css styles
willmcgugan Jan 27, 2025
9eb73f4
parser WIP
willmcgugan Jan 28, 2025
381a1bc
style parse
willmcgugan Jan 30, 2025
1eed935
markup parse and tests
willmcgugan Jan 31, 2025
2795477
markup parsing
willmcgugan Feb 1, 2025
7d228fd
content docs
willmcgugan Feb 2, 2025
8211370
error reporting in playground
willmcgugan Feb 2, 2025
649abb5
fix dim
willmcgugan Feb 3, 2025
bba0b4f
test fixes
willmcgugan Feb 3, 2025
97feb9c
content docs
willmcgugan Feb 3, 2025
3ec59ee
added substution
willmcgugan Feb 4, 2025
b49eb9e
docs and tests
willmcgugan Feb 5, 2025
4e03fea
docs
willmcgugan Feb 6, 2025
32e852b
keys and playground
willmcgugan Feb 6, 2025
bda56ab
words
willmcgugan Feb 6, 2025
3716641
snapshots
willmcgugan Feb 6, 2025
e9dac1c
docstrings
willmcgugan Feb 6, 2025
e676d17
docstrings
willmcgugan Feb 6, 2025
27bbddd
fix artifact
willmcgugan Feb 6, 2025
609a29a
words
willmcgugan Feb 6, 2025
11a7c4e
typing fix
willmcgugan Feb 6, 2025
9944d43
typing fix
willmcgugan Feb 6, 2025
0f3de98
docstring
willmcgugan Feb 6, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
if: ${{ matrix.python-version == '3.8' }}
- name: Upload snapshot report
if: always()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: snapshot-report-textual
path: snapshot_report.html
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added App.ALLOW_SELECT for a global switch to disable text selection https://github.com/Textualize/textual/pull/5409
- Added `DOMNode.query_ancestor` https://github.com/Textualize/textual/pull/5409
- Added selection to Log widget https://github.com/Textualize/textual/pull/5467
- Added `text-wrap` and `text-overflow` CSS values https://github.com/Textualize/textual/pull/5485
- Added Textual markup to replace Rich markup https://github.com/Textualize/textual/pull/5485
- Added `Content.from_markup` https://github.com/Textualize/textual/pull/5485

### Fixed

Expand Down
5 changes: 5 additions & 0 deletions docs/api/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: "textual.content"
---

::: textual.content
5 changes: 5 additions & 0 deletions docs/api/style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: "textual.style"
---

::: textual.style
36 changes: 36 additions & 0 deletions docs/examples/guide/content/content01.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from textual.app import App, ComposeResult
from textual.widgets import Static

TEXT1 = """\
Hello, [bold $text on $primary]World[/]!

[@click=app.notify('Hello, World!')]Click me[/]
"""

TEXT2 = """\
Markup will [bold]not[/bold] be displayed.

Tags will be left in the output.

"""


class ContentApp(App):
CSS = """
Screen {
Static {
height: 1fr;
}
#text1 { background: $primary-muted; }
#text2 { background: $error-muted; }
}
"""

def compose(self) -> ComposeResult:
yield Static(TEXT1, id="text1")
yield Static(TEXT2, id="text2", markup=False) # (1)!


if __name__ == "__main__":
app = ContentApp()
app.run()
5 changes: 5 additions & 0 deletions docs/examples/guide/content/playground.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from textual._markup_playground import MarkupPlayground

if __name__ == "__main__":
app = MarkupPlayground()
app.run()
36 changes: 36 additions & 0 deletions docs/examples/guide/content/renderables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from rich.syntax import Syntax

from textual.app import App, ComposeResult, RenderResult
from textual.reactive import reactive
from textual.widget import Widget


class CodeView(Widget):
"""Widget to display Python code."""

DEFAULT_CSS = """
CodeView { height: auto; }
"""

code = reactive("")

def render(self) -> RenderResult:
# Syntax is a Rich renderable that displays syntax highlighted code
syntax = Syntax(self.code, "python", line_numbers=True, indent_guides=True)
return syntax


class CodeApp(App):
"""App to demonstrate Rich renderables in Textual."""

def compose(self) -> ComposeResult:
with open(__file__) as self_file:
code = self_file.read()
code_view = CodeView()
code_view.code = code
yield code_view


if __name__ == "__main__":
app = CodeApp()
app.run()
2 changes: 1 addition & 1 deletion docs/examples/styles/border_sub_title_align_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def compose(self):
"had to fill up",
"lbl4",
"", # (4)!
"[link=https://textual.textualize.io]Left[/]", # (5)!
"[link='https://textual.textualize.io']Left[/]", # (5)!
)
yield make_label_container( # (6)!
"nine labels", "lbl5", "Title", "Subtitle"
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/styles/link_background.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class LinkBackgroundApp(App):

def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
"Visit the [link='https://textualize.io']Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/styles/link_background_hover.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class LinkHoverBackgroundApp(App):

def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
"Visit the [link='https://textualize.io']Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/styles/link_color.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class LinkColorApp(App):

def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
"Visit the [link='https://textualize.io']Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/styles/link_color_hover.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class LinkHoverColorApp(App):

def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
"Visit the [link='https://textualize.io']Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/styles/link_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class LinkStyleApp(App):

def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
"Visit the [link='https://textualize.io']Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/styles/link_style_hover.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class LinkHoverStyleApp(App):

def compose(self):
yield Label(
"Visit the [link=https://textualize.io]Textualize[/link] website.",
"Visit the [link='https://textualize.io']Textualize[/link] website.",
id="lbl1", # (1)!
)
yield Label(
Expand Down
18 changes: 18 additions & 0 deletions docs/examples/styles/text_overflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from textual.app import App, ComposeResult
from textual.widgets import Static

TEXT = """I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear."""


class WrapApp(App):
CSS_PATH = "text_overflow.tcss"

def compose(self) -> ComposeResult:
yield Static(TEXT, id="static1")
yield Static(TEXT, id="static2")
yield Static(TEXT, id="static3")


if __name__ == "__main__":
app = WrapApp()
app.run()
17 changes: 17 additions & 0 deletions docs/examples/styles/text_overflow.tcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Static {
height: 1fr;
text-wrap: nowrap;
}

#static1 {
text-overflow: clip; # Overflowing text is clipped
background: red 20%;
}
#static2 {
text-overflow: fold; # Overflowing text is folded on to the next line
background: green 20%;
}
#static3 {
text-overflow: ellipsis; # Overflowing text is truncated with an ellipsis
background: blue 20%;
}
17 changes: 17 additions & 0 deletions docs/examples/styles/text_wrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from textual.app import App, ComposeResult
from textual.widgets import Static

TEXT = """I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear."""


class WrapApp(App):
CSS_PATH = "text_wrap.tcss"

def compose(self) -> ComposeResult:
yield Static(TEXT, id="static1")
yield Static(TEXT, id="static2")


if __name__ == "__main__":
app = WrapApp()
app.run()
12 changes: 12 additions & 0 deletions docs/examples/styles/text_wrap.tcss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Static {
height: 1fr;
}

#static1 {
text-wrap: wrap; /* this is the default */
background: blue 20%;
}
#static2 {
text-wrap: nowrap; /* disable wrapping */
background: green 20%;
}
4 changes: 2 additions & 2 deletions docs/guide/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ Consequently `"set_background('blue')"` is a valid action string, but `"set_back

## Links

Actions may be embedded as links within console markup. You can create such links with a `@click` tag.
Actions may be embedded in [markup](./content.md#actions) with the `@click` tag.

The following example mounts simple static text with embedded action links.
The following example mounts simple static text with embedded action links:

=== "actions03.py"

Expand Down
Loading
Loading