Skip to content

Commit 51f94d9

Browse files
committed
Styling the PartialRenderTUI
1 parent abfe6b6 commit 51f94d9

3 files changed

Lines changed: 114 additions & 64 deletions

File tree

plain2code.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ def render(args, run_state: RunState, event_bus: EventBus): # noqa: C901
184184
app = PartialRenderTUI(
185185
plain_module,
186186
partial_render,
187+
system_config.client_version,
188+
run_state.render_id,
187189
css_path="styles.css",
188190
)
189191
partial_render_choice = app.run()

tui/partial_render_tui.py

Lines changed: 89 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,78 @@
11
from textual import on
22
from textual.app import App, ComposeResult
3-
from textual.containers import Horizontal, Vertical
4-
from textual.widgets import Label, ListItem, ListView
3+
from textual.binding import Binding
4+
from textual.containers import Vertical, VerticalScroll
5+
from textual.widgets import ContentSwitcher, Label, ListItem, ListView, Static
56

67
import plain_spec
78
from partial_rendering import PartialRender, PartialRenderChoice
89
from plain_modules import PlainModule
10+
from tui.components import CustomFooter, TUIComponents
911

1012

1113
class PartialRenderTUI(App):
12-
def __init__(self, plain_module: PlainModule, partial_render: PartialRender, **kwargs):
14+
BINDINGS = [
15+
Binding("ctrl+o", "toggle_expand", "Expand/Collapse", show=False),
16+
Binding("ctrl+c", "quit", "Quit", show=False),
17+
Binding("ctrl+d", "quit", "Quit", show=False),
18+
]
19+
20+
def __init__(
21+
self,
22+
plain_module: PlainModule,
23+
partial_render: PartialRender,
24+
state_machine_version: str,
25+
render_id: str,
26+
**kwargs,
27+
):
1328
super().__init__(**kwargs)
1429
self.plain_module = plain_module
1530
self.partial_render = partial_render
1631
self.choices = {} # populated in on_mount
32+
self.state_machine_version = state_machine_version
33+
self.render_id = render_id
34+
self._expandable_labels: list[dict] = []
1735

1836
def compose(self) -> ComposeResult:
19-
yield Vertical(
20-
Label("[bold]--- Partial Render Detected ---[/bold]", classes="highlight"),
21-
Horizontal(
22-
Vertical(id="info-panel-left"),
23-
Vertical(id="info-panel-right"),
24-
id="info-panel-columns",
25-
),
26-
id="info-panel",
27-
)
28-
yield ListView(id="choice-list")
37+
with ContentSwitcher(id="content-switcher", initial=TUIComponents.DASHBOARD_VIEW.value):
38+
with Vertical(id=TUIComponents.DASHBOARD_VIEW.value):
39+
with VerticalScroll():
40+
yield Static(
41+
f"[#FFFFFF]*codeplain[/#FFFFFF] [#888888](v{self.state_machine_version})[/#888888]",
42+
id="codeplain-header",
43+
classes="codeplain-header",
44+
)
45+
yield Vertical(id="info-panel")
46+
yield ListView(id="choice-list")
47+
yield CustomFooter(render_id=self.render_id)
2948

3049
def on_mount(self) -> None:
3150
pr = self.partial_render
3251
pm = self.plain_module
3352

34-
# Info labels — left side
35-
left = self.query_one("#info-panel-left", Vertical)
36-
left.mount(Label("[#e0ff6e]Current state:[/]"))
37-
left.mount(Label(f"Target module: [{'#79fc96'}]{pm.module_name}[/]"))
53+
info_panel = self.query_one("#info-panel", Vertical)
54+
info_panel.mount(Label("module status", classes="rendering-info-title"))
3855

39-
if pr.change_type == "spec_change":
40-
left.mount(Label(f"Detected spec change of module [{'#79fc96'}]{pr.last_render_module.module_name}[/]"))
41-
elif pr.change_type == "code_change":
42-
left.mount(Label(f"Detected code change of module [{'#79fc96'}]{pr.last_render_module.module_name}[/]"))
56+
info_box = Vertical(classes="rendering-info-box")
57+
info_panel.mount(info_box)
4358

59+
info_box.mount(Label(f"module: {pr.last_render_module.module_name}", classes="rendering-info-row"))
4460
if pr.last_render_frid is None or pr.last_render_module.is_module_fully_rendered():
45-
left.mount(Label(f"Module [{'#79fc96'}]{pr.last_render_module.module_name}[/] was fully rendered."))
61+
info_box.mount(Label("module fully rendered", classes="rendering-info-row"))
4662
else:
47-
left.mount(
48-
Label(
49-
f"Module [{'#79fc96'}]{pr.last_render_module.module_name}[/] is partially rendered. Last fully rendered functionality was [{'#79fc96'}]{pr.last_render_frid}[/]"
50-
)
51-
)
63+
frid = pr.last_render_frid
64+
functionality = pr.last_render_module.plain_source[plain_spec.FUNCTIONAL_REQUIREMENTS][int(frid) - 1][
65+
"markdown"
66+
]
67+
label = Label("", classes="rendering-info-row")
68+
info_box.mount(label)
69+
self._register_expandable(label, f"functionality {frid}:", functionality)
5270

5371
# Build choices (same logic as original)
5472
choice_idx = 1
5573
if pr.last_render_frid is not None:
5674
next_frid, next_module = pm.get_next_frid(pr.last_render_frid, pr.last_render_module.module_name)
5775

58-
functionality = next_module.plain_source[plain_spec.FUNCTIONAL_REQUIREMENTS][int(next_frid) - 1]
59-
# Placeholder — right side
60-
61-
right = self.query_one("#info-panel-right", Vertical)
62-
right.mount(Label("[#e0ff6e]Next functionality:[/]"))
63-
right.mount(
64-
Label(f"Module [{'#79fc96'}]{next_module.module_name}[/], functionality [{'#79fc96'}]{next_frid}[/]")
65-
)
66-
right.mount(Label(functionality["markdown"]))
67-
6876
msg = f"Continue from next functionality (module {next_module.module_name}"
6977
if next_frid != plain_spec.get_first_frid(next_module.plain_source):
7078
msg += f" functionality {next_frid})"
@@ -77,14 +85,44 @@ def on_mount(self) -> None:
7785
)
7886
choice_idx += 1
7987

88+
change_box = Vertical(classes="change-box")
89+
info_panel.mount(change_box)
8090
if pr.change:
8191
reason = "spec change" if pr.change_type == "spec_change" else "code change"
92+
93+
title_start = "Spec changes" if pr.change_type == "spec_change" else "Code changes"
94+
change_box.mount(
95+
Label(
96+
f"--- {title_start} detected in required module [#5593FF]{pr.change.module_name}[/] ---",
97+
classes="rendering-info-row",
98+
)
99+
)
100+
change_box.mount(
101+
Label(f"{title_start} in a required module may affect the current module", classes="rendering-info-row")
102+
)
103+
82104
self.choices[str(choice_idx)] = PartialRenderChoice(
83105
module=pr.change,
84106
render_range=None,
85107
msg=f"Re-render {pr.change.module_name} from start due to {reason}",
86108
)
87109
choice_idx += 1
110+
elif pr.last_render_frid is None or pr.last_render_module.is_module_fully_rendered():
111+
change_box.mount(Label("--- Rendering interrupted ---", classes="rendering-info-row"))
112+
change_box.mount(Label("The current module was fully rendered.", classes="rendering-info-row"))
113+
else:
114+
change_box.mount(
115+
Label(
116+
f"--- Rendering interrupted during [#5593FF]functionality {pr.last_render_frid}[/] ---",
117+
classes="rendering-info-row",
118+
)
119+
)
120+
change_box.mount(
121+
Label(
122+
"Resume from the last successfully rendered functionality or start over.",
123+
classes="rendering-info-row",
124+
)
125+
)
88126

89127
first_module = pm.all_required_modules[0]
90128
if first_module.module_name != pr.last_render_module.module_name and (
@@ -101,13 +139,25 @@ def on_mount(self) -> None:
101139

102140
# Populate the ListView
103141
lv = self.query_one("#choice-list", ListView)
104-
self.mount(Label("How would you like to start rendering?", classes="partial-render-question"), before=lv)
142+
self.mount(Label("How would you like to continue?", classes="partial-render-question"), before=lv)
105143
for key, choice in self.choices.items():
106144
lv.append(ListItem(Label(f"[bold]{key}.[/bold] {choice.msg}"), id=f"choice-{key}"))
145+
lv.focus()
146+
147+
def _register_expandable(self, label: Label, prefix: str, full_text: str) -> None:
148+
first_line = full_text[:20]
149+
short = f"{prefix} {first_line} [#888](ctrl+o to expand)[/]"
150+
full = f"{prefix} {full_text} [#888](ctrl+o to collapse)[/]"
151+
label.update(short)
152+
self._expandable_labels.append({"label": label, "short": short, "full": full, "expanded": False})
153+
154+
def action_toggle_expand(self) -> None:
155+
for entry in self._expandable_labels:
156+
entry["expanded"] = not entry["expanded"]
157+
entry["label"].update(entry["full"] if entry["expanded"] else entry["short"])
107158

108159
@on(ListView.Selected)
109160
def on_choice_selected(self, event: ListView.Selected) -> None:
110-
# Extract the key from the widget id ("choice-1" → "1")
111161
key = event.item.id.split("-", 1)[1]
112162
self.selected_choice = self.choices[key]
113163
self.exit(self.selected_choice)

tui/styles.css

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -426,40 +426,38 @@ CustomFooter.footer-state-paused {
426426
/* Partial Render TUI Styles */
427427
#info-panel {
428428
height: auto;
429-
border: solid #e0ff6e;
430-
padding: 1 2;
431-
margin: 1 2;
432429
}
433-
#info-panel .highlight {
434-
color: #79fc96;
435-
margin-bottom: 1;
436-
}
437-
#info-panel-columns {
430+
431+
ListView {
438432
height: auto;
433+
background: transparent;
439434
}
440-
#info-panel-left {
441-
width: auto;
442-
height: auto;
443-
margin-right: 8;
435+
ListView:focus {
436+
background: transparent;
444437
}
445-
#info-panel-left Label {
446-
color: $text-muted;
438+
ListItem {
439+
background: transparent;
440+
color: #fff;
447441
}
448-
#info-panel-right {
449-
width: auto;
450-
height: auto;
442+
ListItem:hover {
443+
background: transparent;
451444
}
452-
#info-panel-right Label {
453-
color: $text-muted;
445+
ListItem.-highlight {
446+
background: #333;
447+
color: #fff;
454448
}
455-
ListView {
456-
margin: 0 2;
449+
450+
.partial-render-question {
451+
margin: 0 1;
457452
height: auto;
458453
}
459-
ListItem {
460-
padding: 0 1;
454+
455+
.change-box {
456+
margin: 1 1;
457+
height: auto;
461458
}
462-
.partial-render-question {
463-
margin: 1 2;
459+
460+
#choice-list {
461+
margin: 0 1;
464462
height: auto;
465463
}

0 commit comments

Comments
 (0)