feat(ui): Implement Task DAG Visualizer using ImGui tree nodes

This commit is contained in:
2026-02-27 22:51:55 -05:00
parent 6f61496a44
commit 7252d759ef

117
gui_2.py
View File

@@ -2837,7 +2837,7 @@ class App:
# 2. Active Track Info
if self.active_track:
imgui.text(f"Track: {self.active_track.get('title', 'Unknown')}")
imgui.text(f"Track: {self.active_track.description}")
# Progress bar
tickets = self.active_tickets
@@ -2871,49 +2871,96 @@ class App:
imgui.separator()
# 4. Ticket Queue
imgui.text("Ticket Queue")
if imgui.begin_table("mma_tickets", 4, imgui.TableFlags_.borders_inner_h | imgui.TableFlags_.resizable):
imgui.table_setup_column("ID", imgui.TableColumnFlags_.width_fixed, 80)
imgui.table_setup_column("Target", imgui.TableColumnFlags_.width_stretch)
imgui.table_setup_column("Status", imgui.TableColumnFlags_.width_fixed, 100)
imgui.table_setup_column("Actions", imgui.TableColumnFlags_.width_fixed, 120)
imgui.table_headers_row()
# 4. Task DAG Visualizer
imgui.text("Task DAG")
if self.active_track:
tickets_by_id = {t.get('id'): t for t in self.active_tickets}
all_ids = set(tickets_by_id.keys())
# Build children map
children_map = {}
for t in self.active_tickets:
tid = t.get('id', '??')
imgui.table_next_row()
imgui.table_next_column()
imgui.text(str(tid))
for dep in t.get('depends_on', []):
if dep not in children_map: children_map[dep] = []
children_map[dep].append(t.get('id'))
imgui.table_next_column()
imgui.text(str(t.get('target_file', 'general')))
# Roots are those whose depends_on elements are NOT in all_ids
roots = []
for t in self.active_tickets:
deps = t.get('depends_on', [])
has_local_dep = any(d in all_ids for d in deps)
if not has_local_dep:
roots.append(t)
imgui.table_next_column()
status = t.get('status', 'pending').upper()
rendered = set()
for root in roots:
self._render_ticket_dag_node(root, tickets_by_id, children_map, rendered)
else:
imgui.text_disabled("No active MMA track.")
if status == 'RUNNING':
imgui.push_style_color(imgui.Col_.text, vec4(255, 255, 0)) # Yellow
elif status == 'COMPLETE':
imgui.push_style_color(imgui.Col_.text, vec4(0, 255, 0)) # Green
elif status == 'BLOCKED' or status == 'ERROR':
imgui.push_style_color(imgui.Col_.text, vec4(255, 0, 0)) # Red
elif status == 'PAUSED':
imgui.push_style_color(imgui.Col_.text, vec4(255, 165, 0)) # Orange
def _render_ticket_dag_node(self, ticket, tickets_by_id, children_map, rendered):
tid = ticket.get('id', '??')
target = ticket.get('target_file', 'general')
status = ticket.get('status', 'pending').upper()
imgui.text(status)
# Determine color
status_color = vec4(200, 200, 200) # Gray (TODO)
if status == 'RUNNING':
status_color = vec4(255, 255, 0) # Yellow
elif status == 'COMPLETE':
status_color = vec4(0, 255, 0) # Green
elif status in ['BLOCKED', 'ERROR']:
status_color = vec4(255, 0, 0) # Red
elif status == 'PAUSED':
status_color = vec4(255, 165, 0) # Orange
if status in ['RUNNING', 'COMPLETE', 'BLOCKED', 'ERROR', 'PAUSED']:
imgui.pop_style_color()
flags = imgui.TreeNodeFlags_.open_on_arrow | imgui.TreeNodeFlags_.open_on_double_click | imgui.TreeNodeFlags_.default_open
children = children_map.get(tid, [])
if not children:
flags |= imgui.TreeNodeFlags_.leaf
imgui.table_next_column()
if imgui.button(f"Retry##{tid}"):
self._cb_ticket_retry(tid)
imgui.same_line()
if imgui.button(f"Skip##{tid}"):
self._cb_ticket_skip(tid)
# Check if already rendered elsewhere to avoid infinite recursion or duplicate subtrees
is_duplicate = tid in rendered
imgui.end_table()
node_open = imgui.tree_node_ex(f"##{tid}", flags)
# Detail View / Tooltip
if imgui.is_item_hovered():
imgui.begin_tooltip()
imgui.text_colored(C_KEY, f"ID: {tid}")
imgui.text_colored(C_LBL, f"Target: {target}")
imgui.text_colored(C_LBL, f"Description:")
imgui.same_line()
imgui.text_wrapped(ticket.get('description', 'N/A'))
deps = ticket.get('depends_on', [])
if deps:
imgui.text_colored(C_LBL, f"Depends on: {', '.join(deps)}")
imgui.end_tooltip()
imgui.same_line()
imgui.text_colored(C_KEY, tid)
imgui.same_line(150)
imgui.text_disabled(str(target))
imgui.same_line(400)
imgui.text_colored(status_color, status)
imgui.same_line(500)
if imgui.button(f"Retry##{tid}"):
self._cb_ticket_retry(tid)
imgui.same_line()
if imgui.button(f"Skip##{tid}"):
self._cb_ticket_skip(tid)
if node_open:
if not is_duplicate:
rendered.add(tid)
for child_id in children:
child = tickets_by_id.get(child_id)
if child:
self._render_ticket_dag_node(child, tickets_by_id, children_map, rendered)
else:
imgui.text_disabled(" (shown above)")
imgui.tree_pop()
def _render_tool_calls_panel(self):
imgui.text("Tool call history")