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 # 2. Active Track Info
if self.active_track: if self.active_track:
imgui.text(f"Track: {self.active_track.get('title', 'Unknown')}") imgui.text(f"Track: {self.active_track.description}")
# Progress bar # Progress bar
tickets = self.active_tickets tickets = self.active_tickets
@@ -2871,49 +2871,96 @@ class App:
imgui.separator() imgui.separator()
# 4. Ticket Queue # 4. Task DAG Visualizer
imgui.text("Ticket Queue") imgui.text("Task DAG")
if imgui.begin_table("mma_tickets", 4, imgui.TableFlags_.borders_inner_h | imgui.TableFlags_.resizable): if self.active_track:
imgui.table_setup_column("ID", imgui.TableColumnFlags_.width_fixed, 80) tickets_by_id = {t.get('id'): t for t in self.active_tickets}
imgui.table_setup_column("Target", imgui.TableColumnFlags_.width_stretch) all_ids = set(tickets_by_id.keys())
imgui.table_setup_column("Status", imgui.TableColumnFlags_.width_fixed, 100)
imgui.table_setup_column("Actions", imgui.TableColumnFlags_.width_fixed, 120)
imgui.table_headers_row()
# Build children map
children_map = {}
for t in self.active_tickets: for t in self.active_tickets:
tid = t.get('id', '??') for dep in t.get('depends_on', []):
imgui.table_next_row() if dep not in children_map: children_map[dep] = []
imgui.table_next_column() children_map[dep].append(t.get('id'))
imgui.text(str(tid))
imgui.table_next_column() # Roots are those whose depends_on elements are NOT in all_ids
imgui.text(str(t.get('target_file', 'general'))) 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() rendered = set()
status = t.get('status', 'pending').upper() 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': def _render_ticket_dag_node(self, ticket, tickets_by_id, children_map, rendered):
imgui.push_style_color(imgui.Col_.text, vec4(255, 255, 0)) # Yellow tid = ticket.get('id', '??')
elif status == 'COMPLETE': target = ticket.get('target_file', 'general')
imgui.push_style_color(imgui.Col_.text, vec4(0, 255, 0)) # Green status = ticket.get('status', 'pending').upper()
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
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']: flags = imgui.TreeNodeFlags_.open_on_arrow | imgui.TreeNodeFlags_.open_on_double_click | imgui.TreeNodeFlags_.default_open
imgui.pop_style_color() children = children_map.get(tid, [])
if not children:
flags |= imgui.TreeNodeFlags_.leaf
imgui.table_next_column() # Check if already rendered elsewhere to avoid infinite recursion or duplicate subtrees
if imgui.button(f"Retry##{tid}"): is_duplicate = tid in rendered
self._cb_ticket_retry(tid)
imgui.same_line()
if imgui.button(f"Skip##{tid}"):
self._cb_ticket_skip(tid)
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): def _render_tool_calls_panel(self):
imgui.text("Tool call history") imgui.text("Tool call history")