151 lines
4.8 KiB
Python
151 lines
4.8 KiB
Python
import json
|
|
|
|
from textual import containers, events, on
|
|
from textual.app import App, ComposeResult
|
|
from textual.content import Content
|
|
from textual.reactive import reactive
|
|
from textual.widgets import Footer, Pretty, Static, TextArea
|
|
|
|
|
|
class MarkupPlayground(App):
|
|
|
|
TITLE = "Markup Playground"
|
|
CSS = """
|
|
Screen {
|
|
layout: vertical;
|
|
#editor {
|
|
width: 1fr;
|
|
height: 1fr;
|
|
border: tab $foreground 50%;
|
|
padding: 1;
|
|
margin: 1 0 0 0;
|
|
&:focus {
|
|
border: tab $primary;
|
|
}
|
|
|
|
}
|
|
#variables {
|
|
width: 1fr;
|
|
height: 1fr;
|
|
border: tab $foreground 50%;
|
|
padding: 1;
|
|
margin: 1 0 0 1;
|
|
&:focus {
|
|
border: tab $primary;
|
|
}
|
|
}
|
|
#variables.-bad-json {
|
|
border: tab $error;
|
|
}
|
|
#results-container {
|
|
border: tab $success;
|
|
&.-error {
|
|
border: tab $error;
|
|
}
|
|
overflow-y: auto;
|
|
}
|
|
#results {
|
|
padding: 1 1;
|
|
width: 1fr;
|
|
}
|
|
#spans-container {
|
|
border: tab $success;
|
|
overflow-y: auto;
|
|
margin: 0 0 0 1;
|
|
}
|
|
#spans {
|
|
padding: 1 1;
|
|
width: 1fr;
|
|
}
|
|
HorizontalGroup {
|
|
height: 1fr;
|
|
}
|
|
}
|
|
"""
|
|
AUTO_FOCUS = "#editor"
|
|
|
|
BINDINGS = [
|
|
("f1", "toggle('show_variables')", "Variables"),
|
|
("f2", "toggle('show_spans')", "Spans"),
|
|
]
|
|
variables: reactive[dict[str, object]] = reactive({})
|
|
|
|
show_variables = reactive(True)
|
|
show_spans = reactive(False)
|
|
|
|
def compose(self) -> ComposeResult:
|
|
with containers.HorizontalGroup():
|
|
yield (editor := TextArea(id="editor", soft_wrap=False))
|
|
yield (variables := TextArea("", id="variables", language="json"))
|
|
editor.border_title = "Markup"
|
|
variables.border_title = "Variables (JSON)"
|
|
|
|
with containers.HorizontalGroup():
|
|
with containers.VerticalScroll(id="results-container") as container:
|
|
yield Static(id="results")
|
|
container.border_title = "Output"
|
|
with containers.VerticalScroll(id="spans-container") as container:
|
|
yield Pretty([], id="spans")
|
|
container.border_title = "Spans"
|
|
|
|
yield Footer()
|
|
|
|
def watch_show_variables(self, show_variables: bool) -> None:
|
|
self.query_one("#variables").display = show_variables
|
|
|
|
def watch_show_spans(self, show_spans: bool) -> None:
|
|
self.query_one("#spans-container").display = show_spans
|
|
|
|
@on(TextArea.Changed, "#editor")
|
|
def on_markup_changed(self, event: TextArea.Changed) -> None:
|
|
self.update_markup()
|
|
|
|
def update_markup(self) -> None:
|
|
results = self.query_one("#results", Static)
|
|
editor = self.query_one("#editor", TextArea)
|
|
spans = self.query_one("#spans", Pretty)
|
|
try:
|
|
content = Content.from_markup(editor.text, **self.variables)
|
|
results.update(content)
|
|
spans.update(content.spans)
|
|
except Exception:
|
|
from rich.traceback import Traceback
|
|
|
|
results.update(Traceback())
|
|
spans.update([])
|
|
|
|
self.query_one("#results-container").add_class("-error").scroll_end(
|
|
animate=False
|
|
)
|
|
else:
|
|
self.query_one("#results-container").remove_class("-error")
|
|
|
|
def watch_variables(self, variables: dict[str, object]) -> None:
|
|
self.update_markup()
|
|
|
|
@on(TextArea.Changed, "#variables")
|
|
def on_variables_change(self, event: TextArea.Changed) -> None:
|
|
variables_text_area = self.query_one("#variables", TextArea)
|
|
try:
|
|
variables = json.loads(variables_text_area.text)
|
|
except Exception as error:
|
|
variables_text_area.add_class("-bad-json")
|
|
self.variables = {}
|
|
else:
|
|
variables_text_area.remove_class("-bad-json")
|
|
self.variables = variables
|
|
|
|
@on(events.DescendantBlur, "#variables")
|
|
def on_variables_blur(self) -> None:
|
|
variables_text_area = self.query_one("#variables", TextArea)
|
|
try:
|
|
variables = json.loads(variables_text_area.text)
|
|
except Exception as error:
|
|
if not variables_text_area.has_class("-bad-json"):
|
|
self.notify(f"Bad JSON: ${error}", title="Variables", severity="error")
|
|
variables_text_area.add_class("-bad-json")
|
|
else:
|
|
variables_text_area.remove_class("-bad-json")
|
|
variables_text_area.text = json.dumps(variables, indent=4)
|
|
self.variables = variables
|