diff --git a/scripts/generate_visual_guide.py b/scripts/generate_visual_guide.py new file mode 100644 index 0000000..6070b19 --- /dev/null +++ b/scripts/generate_visual_guide.py @@ -0,0 +1,579 @@ +import abc +import collections +from pathlib import Path + +import jinja2 + +from qtile_bonsai.core.nodes import Node, Pane +from qtile_bonsai.core.tree import Tree +from qtile_bonsai.core.utils import to_snake_case + + +jinja_env = jinja2.Environment( + loader=jinja2.FileSystemLoader("templates"), autoescape=True +) + +index_tmpl_path = Path("visual_guide/index.template.html") +index_html_path = Path("static/visual_guide/index.html") + +_examples_registry: list[type["Example"]] = [] + + +class ExamplePane(Pane): + label: str + focused: bool + min_size: int = 10 + + +class ExampleTree(Tree): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.command: str | None = None + self._pane_label_seq = "A" + + def focus(self, pane: ExamplePane): + super().focus(pane) + for p in self.iter_panes(): + p.focused = False + pane.focused = True + + def create_pane(self, *args, **kwargs) -> ExamplePane: + p = super().create_pane(*args, **kwargs) + ep = ExamplePane( + p.principal_rect, + margin=p.box.margin, + border=p.box.border, + padding=p.box.padding, + ) + ep.id = p.id + ep.label = self._get_next_pane_label() + self.focus(ep) + return ep + + def clone(self) -> "ExampleTree": + Node.reset_id_seq() + return super().clone() + + def _get_next_pane_label(self): + label = self._pane_label_seq + self._pane_label_seq = chr(ord(self._pane_label_seq) + 1) + return label + + +def make_tree(): + Node.reset_id_seq() + tree = ExampleTree(300, 200) + tree.set_config("tab_bar.hide_when", "single_tab") + tree.set_config("tab_bar.height", 15) + return tree + + +class Example(metaclass=abc.ABCMeta): + section: str = "" + template_path: str = "visual_guide/examples/1_to_n.template.html" + + @abc.abstractmethod + def build_context_fragment(self) -> dict: + pass + + def __init_subclass__(cls, **kwargs): + super().__init_subclass__(**kwargs) + if cls not in _examples_registry: + _examples_registry.append(cls) + + @classmethod + def id(cls): + return to_snake_case(cls.__name__.partition("Eg")[2]) + + def build_context(self) -> dict: + cxt = { + "id": self.id(), + "template_path": self.template_path, + } + cxt.update(self.build_context_fragment()) + return cxt + + +class EgSplits1(Example): + section = "Splits" + + def build_context_fragment(self): + lhs = make_tree() + lhs.tab() + + rhs1 = lhs.clone() + rhs1.split(rhs1.node(4), "x") + rhs1.command = 'spawn_split(program, "x")' + + rhs2 = lhs.clone() + rhs2.split(rhs2.node(4), "y") + rhs2.command = 'spawn_split(program, "y")' + + return { + "lhs": lhs, + "rhs_items": [rhs1, rhs2], + } + + +class EgTabs1(Example): + section = "Tabs" + + def build_context_fragment(self): + lhs = make_tree() + lhs.tab() + + rhs = lhs.clone() + rhs.tab() + rhs.command = "spawn_tab(program)" + + return { + "lhs": lhs, + "rhs_items": [rhs], + } + + +class EgTabs2(Example): + section = "Tabs" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "x") + lp3 = lhs.split(lp2, "y") + + rhs = lhs.clone() + rhs.tab(rhs.node(lp3.id), new_level=True) + rhs.command = "spawn_tab(program, new_level=True)" + + return { + "lhs": lhs, + "rhs_items": [rhs], + } + + +class EgTabs3(Example): + section = "Tabs" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "x") + lp3 = lhs.split(lp2, "y") + lp4 = lhs.tab(lp3, new_level=True) + + rhs1 = lhs.clone() + rhs1.tab(rhs1.node(lp4.id)) + rhs1.command = "spawn_tab(program)" + + rhs2 = lhs.clone() + rhs2.tab(rhs2.node(lp4.id), level=1) + rhs2.command = "spawn_tab(program, level=1)" + + return { + "lhs": lhs, + "rhs_items": [rhs1, rhs2], + } + + +class EgMergeToSubtab1(Example): + section = "Merge to Subtab" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "y") + lp3 = lhs.split(lp2, "x") + _ = lhs.split(lp3, "x", normalize=True) + lhs.focus(lp3) + + rhs1 = lhs.clone() + rhs1.merge_with_neighbor_to_subtab(rhs1.node(lp3.id), "right", normalize=True) + rhs1.command = 'merge_to_subtab("right")' + rhs1.focus(rhs1.node(lp3.id)) + + rhs2 = lhs.clone() + rhs2.merge_with_neighbor_to_subtab(rhs2.node(lp3.id), "left", normalize=True) + rhs2.command = 'merge_to_subtab("left")' + rhs2.focus(rhs2.node(lp3.id)) + + rhs3 = lhs.clone() + rhs3.merge_with_neighbor_to_subtab(rhs3.node(lp3.id), "up", normalize=True) + rhs3.command = 'merge_to_subtab("up")' + rhs3.focus(rhs3.node(lp3.id)) + + return { + "lhs": lhs, + "rhs_items": [rhs1, rhs2, rhs3], + } + + +class EgMergeToSubtab2(Example): + section = "Merge to Subtab" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "y") + lp3 = lhs.split(lp2, "x") + lp4 = lhs.split(lp3, "x", normalize=True) + _ = lhs.tab(lp4, new_level=True) + lhs.focus(lp3) + + rhs1 = lhs.clone() + rhs1.merge_with_neighbor_to_subtab(rhs1.node(lp3.id), "right", normalize=True) + rhs1.command = 'merge_to_subtab("right")' + rhs1.focus(rhs1.node(lp3.id)) + + return { + "lhs": lhs, + "rhs_items": [rhs1], + } + + +class EgPushIn1(Example): + section = "Push In" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "x", ratio=0.33) + lp3 = lhs.split(lp2, "y") + lp4 = lhs.split(lp3.parent, "x") + _ = lhs.split(lp4, "y") + lhs.focus(lp3) + + rhs1 = lhs.clone() + rhs1.push_in_with_neighbor( + rhs1.node(lp3.id), + "right", + src_selection="mru_deepest", + dest_selection="mru_largest", + normalize=True, + ) + rhs1.command = 'push_in("right")' + rhs1.focus(rhs1.node(lp3.id)) + + rhs2 = lhs.clone() + rhs2.push_in_with_neighbor( + rhs2.node(lp3.id), + "left", + src_selection="mru_deepest", + dest_selection="mru_deepest", + ) + rhs2.command = 'push_in("left")' + rhs2.focus(rhs2.node(lp3.id)) + + return { + "lhs": lhs, + "rhs_items": [rhs1, rhs2], + } + + +class EgPullOut1(Example): + section = "Pull Out" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "x") + lp3 = lhs.split(lp2, "y") + lp4 = lhs.split(lp3, "x") + lp5 = lhs.split(lp4, "y") + lhs.focus(lp5) + + rhs1 = lhs.clone() + rhs1.pull_out(rhs1.node(lp5.id), normalize=True) + rhs1.focus(rhs1.node(lp5.id)) + rhs1.command = "pull_out()" + + rhs2 = lhs.clone() + rhs2.pull_out(rhs2.node(lp5.id), position="next", normalize=True) + rhs2.focus(rhs2.node(lp5.id)) + rhs2.command = 'pull_out(position="next")' + + return { + "lhs": lhs, + "rhs_items": [rhs1, rhs2], + } + + +class EgPullOut2(Example): + section = "Pull Out" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "x") + lp3 = lhs.split(lp2, "y") + lp4 = lhs.split(lp3, "x", ratio=0.33) + lp5 = lhs.split(lp4, "x") + lhs.focus(lp5) + + rhs1 = lhs.clone() + rhs1.pull_out(rhs1.node(lp5.id), normalize=True) + rhs1.focus(rhs1.node(lp5.id)) + rhs1.command = "pull_out()" + + return { + "lhs": lhs, + "rhs_items": [rhs1], + } + + +class EgPullOut3(Example): + section = "Pull Out" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "x") + lp3 = lhs.split(lp2, "y") + lp4 = lhs.tab(lp3, new_level=True) + lhs.focus(lp4) + + rhs1 = lhs.clone() + rhs1.pull_out(rhs1.node(lp4.id), normalize=True) + rhs1.focus(rhs1.node(lp4.id)) + rhs1.command = "pull_out()" + + return { + "lhs": lhs, + "rhs_items": [rhs1], + } + + +class EgPullOutToTab1(Example): + section = "Pull Out to Tab" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "x") + lp3 = lhs.split(lp2, "y") + lhs.focus(lp3) + + rhs1 = lhs.clone() + rhs1.pull_out_to_tab( + rhs1.node(lp3.id), + normalize=True, + ) + rhs1.command = "pull_out_to_tab()" + rhs1.focus(rhs1.node(lp3.id)) + + return { + "lhs": lhs, + "rhs_items": [rhs1], + } + + +class EgPullOutToTab2(Example): + section = "Pull Out to Tab" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "x") + lp3 = lhs.split(lp2, "y") + lp4 = lhs.tab(lp3, new_level=True) + lp5 = lhs.split(lp4, "x") + lhs.focus(lp5) + + rhs1 = lhs.clone() + rhs1.pull_out_to_tab( + rhs1.node(lp5.id), + normalize=True, + ) + rhs1.command = "pull_out_to_tab()" + rhs1.focus(rhs1.node(lp5.id)) + + return { + "lhs": lhs, + "rhs_items": [rhs1], + } + + +class EgPullOutToTab3(Example): + section = "Pull Out to Tab" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "x") + lp3 = lhs.split(lp2, "y") + lp4 = lhs.tab(lp3, new_level=True) + lp5 = lhs.tab(lp4) + lhs.focus(lp5) + + rhs1 = lhs.clone() + rhs1.pull_out_to_tab( + rhs1.node(lp5.id), + normalize=True, + ) + rhs1.command = "pull_out_to_tab()" + rhs1.focus(rhs1.node(lp5.id)) + + return { + "lhs": lhs, + "rhs_items": [rhs1], + } + + +class EgAdvanced1(Example): + section = "Advanced Options for Branch Selection" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "x", ratio=0.33) + lp3 = lhs.split(lp2, "y", ratio=0.25) + lp4 = lhs.split(lp3.parent, "x") + _ = lhs.split(lp4, "y", ratio=0.6) + lhs.focus(lp3) + + rhs1 = lhs.clone() + rhs1.merge_with_neighbor_to_subtab( + rhs1.node(lp3.id), + "right", + src_selection="mru_deepest", + dest_selection="mru_deepest", + ) + rhs1.command = 'merge_to_subtab(\n "right",\n src_selection="mru_deepest",\n dest_selection="mru_deepest",\n)' + rhs1.focus(rhs1.node(lp3.id)) + + rhs2 = lhs.clone() + rhs2.merge_with_neighbor_to_subtab( + rhs2.node(lp3.id), + "right", + src_selection="mru_deepest", + dest_selection="mru_largest", + ) + rhs2.command = 'merge_to_subtab(\n "right",\n src_selection="mru_deepest",\n dest_selection="mru_largest",\n)' + rhs2.focus(rhs2.node(lp3.id)) + + rhs3 = lhs.clone() + rhs3.merge_with_neighbor_to_subtab( + rhs3.node(lp3.id), + "right", + src_selection="mru_largest", + dest_selection="mru_deepest", + ) + rhs3.command = 'merge_to_subtab(\n "right",\n src_selection="mru_largest",\n dest_selection="mru_deepest",\n)' + rhs3.focus(rhs3.node(lp3.id)) + + rhs4 = lhs.clone() + rhs4.merge_with_neighbor_to_subtab( + rhs4.node(lp3.id), + "right", + src_selection="mru_largest", + dest_selection="mru_largest", + ) + rhs4.command = 'merge_to_subtab(\n "right",\n src_selection="mru_largest",\n dest_selection="mru_largest",\n)' + rhs4.focus(rhs4.node(lp3.id)) + + return { + "lhs": lhs, + "rhs_items": [rhs1, rhs2, rhs3, rhs4], + "subtitle": "Applicable to merge_to_subtab(), push_in(), pull_out()", + } + + +class EgAdvanced2(Example): + section = "Advanced Options for Branch Selection" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "y", ratio=0.25) + lp3 = lhs.split(lp2.parent, "x", ratio=0.33) + _ = lhs.split(lp3, "y") + lp5 = lhs.split(lp3.parent, "x") + lp6 = lhs.tab(lp5, new_level=True) + _ = lhs.split(lp6, "x") + lhs.focus(lp3) + + rhs1 = lhs.clone() + rhs1.merge_with_neighbor_to_subtab( + rhs1.node(lp3.id), + "right", + src_selection="mru_deepest", + dest_selection="mru_subtab_else_deepest", + ) + rhs1.focus(rhs1.node(lp3.id)) + rhs1.command = 'merge_to_subtab(\n "right",\n dest_selection="mru_subtab_else_deepest",\n)' + + rhs2 = lhs.clone() + rhs2.merge_with_neighbor_to_subtab( + rhs2.node(lp3.id), + "left", + src_selection="mru_deepest", + dest_selection="mru_subtab_else_deepest", + ) + rhs2.focus(rhs2.node(lp3.id)) + rhs2.command = 'merge_to_subtab(\n "left",\n dest_selection="mru_subtab_else_deepest",\n)' + + rhs3 = lhs.clone() + rhs3.merge_with_neighbor_to_subtab( + rhs3.node(lp3.id), + "left", + src_selection="mru_deepest", + dest_selection="mru_subtab_else_deepest", + ) + rhs3.focus(rhs2.node(lp3.id)) + rhs3.command = 'merge_to_subtab(\n "left",\n dest_selection="mru_subtab_else_deepest",\n)' + + return { + "lhs": lhs, + "rhs_items": [rhs1, rhs2], + } + + +class EgAdvanced3(Example): + section = "Advanced Options for Branch Selection" + + def build_context_fragment(self): + lhs = make_tree() + lp1 = lhs.tab() + lp2 = lhs.split(lp1, "y", ratio=0.25) + lp3 = lhs.split(lp2.parent, "x", ratio=0.33) + _ = lhs.split(lp3, "y") + lp5 = lhs.split(lp3.parent, "x") + lp6 = lhs.split(lp5, "y", ratio=0.3) + _ = lhs.tab(lp6, new_level=True) + lhs.focus(lp3) + + rhs1 = lhs.clone() + rhs1.merge_with_neighbor_to_subtab( + rhs1.node(lp3.id), "right", dest_selection="mru_subtab_else_largest" + ) + rhs1.focus(rhs1.node(lp3.id)) + rhs1.command = 'merge_to_subtab(\n "right",\n dest_selection="mru_subtab_else_largest",\n)' + + rhs2 = lhs.clone() + rhs2.merge_with_neighbor_to_subtab( + rhs2.node(lp3.id), "left", dest_selection="mru_subtab_else_largest" + ) + rhs2.focus(rhs2.node(lp3.id)) + rhs2.command = 'merge_to_subtab(\n "left",\n dest_selection="mru_subtab_else_largest",\n)' + + return { + "lhs": lhs, + "rhs_items": [rhs1, rhs2], + } + + +def main(): + examples = [Eg() for Eg in _examples_registry] + + grouped_examples = collections.defaultdict(list) + for eg in examples: + grouped_examples[eg.section].append(eg.build_context()) + + index_tmpl = jinja_env.get_template(str(index_tmpl_path)) + index_html = index_tmpl.render({"sections": grouped_examples}) + + index_html_path.write_text(index_html) + + +if __name__ == "__main__": + main() diff --git a/static/visual_guide/index.css b/static/visual_guide/index.css new file mode 100644 index 0000000..a3a0017 --- /dev/null +++ b/static/visual_guide/index.css @@ -0,0 +1,140 @@ +body { + background: var(--bg_s); +} + +.guide { + margin: 5% 15%; + padding: 0; +} + +.main-header { + text-align: center; +} +.main-header h1 { + margin: 0.1rem; +} +.main-header .subtitle { + font-size: 1.4rem; + margin: 0; +} + +footer { + text-align: center; + font-size: 0.85rem; +} + + +.section, .section ul { + list-style: none; + margin: 0; + padding: 0; +} + + +.section .title { + text-align: center; +} +.section h2 { + margin: 0; +} +.section .subtitle { + text-align: center; + font-size: 0.8rem; + margin: 0.2rem; + margin-bottom: 2rem; +} + +.example { + display: grid; + place-items: center; + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: 1fr; + grid-row-gap: 3rem; + margin: 3rem 1rem; +} + + +.tree { + +} + +.tree.rhs { + grid-column: 3; +} + +.command-label { + margin-bottom: 2rem; + font-size: 0.75rem; +} + +.example-advanced_1 .command-label { + font-size: 0.7rem; + margin-bottom: 1rem; + align-self: start; +} +.example-advanced_2 .command-label { + font-size: 0.7rem; + margin-top: 1rem; + align-self: start; +} +.example-advanced_3 .command-label { + font-size: 0.7rem; + margin-top: 1rem; + align-self: start; +} + +.node-tc { + +} + +.tc-bar { + box-sizing: border-box; + display: flex; + flex-direction: row; + border: 1px solid black; + border-bottom: 0; + font-size: 0.6rem; + background: var(--bg2); +} +.tc-bar-item { + box-sizing: border-box; + width: 1.7rem; + padding: 1px 3px; + border-right: 1px solid black; + display: flex; + justify-content: center; + align-items: center; +} +.tc-bar-item.active { + color: var(--red-dim); +} + +.node-t { + margin: 0; + padding: 0; +} + + +.node-sc { + display: flex; +} +.node-sc.sc-x { + flex-direction: row; +} +.node-sc.sc-y { + flex-direction: column; +} + +.node-p { + box-sizing: border-box; + display: flex; + justify-content: center; + align-items: center; + border: 1px solid black; + font-size: 1.2rem; + + background: var(--bg_h); +} +.node-p.focused { + color: var(--red-dim); +} diff --git a/static/visual_guide/index.html b/static/visual_guide/index.html new file mode 100644 index 0000000..f474756 --- /dev/null +++ b/static/visual_guide/index.html @@ -0,0 +1,5194 @@ + + + + + + + + + Visual Guide for Qtile Bonsai + + + + +
+

Qtile Bonsai

+

Visual Guide

+
+ + + + + + + + \ No newline at end of file diff --git a/static/visual_guide/themes.css b/static/visual_guide/themes.css new file mode 100644 index 0000000..4dd16ef --- /dev/null +++ b/static/visual_guide/themes.css @@ -0,0 +1,155 @@ +:root { + --bg_h: #f9f5d7; + --bg: #fbf1c7; + --bg_s: #f2e5bc; + --bg1: #ebdbb2; + --bg2: #d5c4a1; + --bg3: #bdae93; + --bg4: #a89984; + + --fg: #282828; + --fg1: #3c3836; + --fg2: #504945; + --fg3: #665c54; + --fg4: #7c6f64; + + --red: #9d0006; + --green: #79740e; + --yellow: #b57614; + --blue: #076678; + --purple: #8f3f71; + --aqua: #427b58; + --orange: #af3a03; + --gray: #928374; + + --red-dim: #cc2412; + --green-dim: #98971a; + --yellow-dim: #d79921; + --blue-dim: #458598; + --purple-dim: #b16286; + --aqua-dim: #689d6a; + --orange-dim: #d65d0e; + --gray-dim: #7c6f64; +} + +/* Normal Colors */ + +.red { + color: var(--red); +} + +.green { + color: var(--green); +} + +.yellow { + color: var(--yellow); +} + +.blue { + color: var(--blue); +} + +.purple { + color: var(--purple); +} + +.aqua { + color: var(--aqua); +} + +.gray { + color: var(--gray); +} + +.orange { + color: var(--orange); +} + +/* Dim Colors */ + +.red-dim { + color: var(--red-dim); +} + +.green-dim { + color: var(--green-dim); +} + +.yellow-dim { + color: var(--yellow-dim); +} + +.blue-dim { + color: var(--blue-dim); +} + +.purple-dim { + color: var(--purple-dim); +} + +.aqua-dim { + color: var(--aqua-dim); +} + +.gray-dim { + color: var(--gray-dim); +} + +.orange-dim { + color: var(--orange-dim); +} + +/* Foreground Colors */ + +.fg { + color: var(--fg); +} + +.fg1 { + color: var(--fg1); +} + +.fg2 { + color: var(--fg2); +} + +.fg3 { + color: var(--fg3); +} + +.fg4 { + color: var(--fg4); +} + +/* Background Colors */ + +.bg-hard { + color: var(--bg_h); +} + +.bg { + color: var(--bg); +} + +.bg-soft { + color: var(--bg_s); +} + +.bg1 { + color: var(--bg1); +} + +.bg2 { + color: var(--bg2); +} + +.bg3 { + color: var(--bg3); +} + +.bg4 { + color: var(--bg4); +} + + diff --git a/static/visual_guide/visual_guide.png b/static/visual_guide/visual_guide.png new file mode 100644 index 0000000..0fd8eda Binary files /dev/null and b/static/visual_guide/visual_guide.png differ diff --git a/templates/visual_guide/examples/1_to_n.template.html b/templates/visual_guide/examples/1_to_n.template.html new file mode 100644 index 0000000..cdad59f --- /dev/null +++ b/templates/visual_guide/examples/1_to_n.template.html @@ -0,0 +1,41 @@ + + +
{{ example.subtitle }}
+
+ {{ render_tree(example.lhs, "lhs") }} + {% for rhs in example.rhs_items %} +
{{ rhs.command }}
+ {{ render_tree(rhs, "rhs") }} + {% endfor %} +
+ + diff --git a/templates/visual_guide/index.template.html b/templates/visual_guide/index.template.html new file mode 100644 index 0000000..7eafcdc --- /dev/null +++ b/templates/visual_guide/index.template.html @@ -0,0 +1,36 @@ +{% from 'visual_guide/tree.template.html' import render_tree %} + + + + + + + + Visual Guide for Qtile Bonsai + + + + +
+

Qtile Bonsai

+

Visual Guide

+
+ + + + + + + + diff --git a/templates/visual_guide/tree.template.html b/templates/visual_guide/tree.template.html new file mode 100644 index 0000000..da91f1f --- /dev/null +++ b/templates/visual_guide/tree.template.html @@ -0,0 +1,56 @@ +{% macro render_tc(node) %} +
+ {% if not node.tab_bar.is_hidden %} + + {% endif %} + {% if node.active_child %} + {{ render_node(node.active_child) }} + {% endif %} +
+{% endmacro %} + +{% macro render_t(node) %} +
+ {% for n in node.children %} + {{ render_node(n) }} + {% endfor %} +
+{% endmacro %} + +{% macro render_sc(node) %} + +{% endmacro %} + +{% macro render_p(node) %} +
+ {{ node.label }} +
+{% endmacro %} + + +{% macro render_node(node) %} + {% if node.abbrv() == "tc" %} + {{ render_tc(node) }} + {% elif node.abbrv() == "t" %} + {{ render_t(node) }} + {% elif node.abbrv() == "sc" %} + {{ render_sc(node) }} + {% elif node.abbrv() == "p" %} + {{ render_p(node) }} + {% endif %} +{% endmacro %} + + +{% macro render_tree(tree, extra_classes="") %} +
+ {{ render_node(tree.root) }} +
+{% endmacro %}