ai-station/.venv/lib/python3.12/site-packages/textual/_markup_playground.py

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