Broken: Fixing this crap
This commit is contained in:
+504
-408
@@ -992,7 +992,7 @@ class App:
|
|||||||
if p not in self.screenshots: self.screenshots.append(p)
|
if p not in self.screenshots: self.screenshots.append(p)
|
||||||
return
|
return
|
||||||
|
|
||||||
def _gui_func__abusrd_try_scope(self) -> None:
|
def _render_main_interface(self) -> None:
|
||||||
self.perf_monitor.start_frame()
|
self.perf_monitor.start_frame()
|
||||||
self._autofocus_response_tab = self.controller._autofocus_response_tab
|
self._autofocus_response_tab = self.controller._autofocus_response_tab
|
||||||
|
|
||||||
@@ -1095,6 +1095,496 @@ class App:
|
|||||||
else:
|
else:
|
||||||
self._pending_dialog_open = False
|
self._pending_dialog_open = False
|
||||||
|
|
||||||
|
if imgui.begin_popup_modal("Approve PowerShell Command", None, imgui.WindowFlags_.always_auto_resize)[0]:
|
||||||
|
if not dlg:
|
||||||
|
imgui.close_current_popup()
|
||||||
|
|
||||||
|
def _render_window_if_open(self, name: str, render_func: Callable[[], None], flag_condition: bool = True) -> None:
|
||||||
|
"""Helper to render a window only if its toggle is active."""
|
||||||
|
if not flag_condition or not self.show_windows.get(name, False): return
|
||||||
|
with imscope.window(name, self.show_windows[name]) as (exp, opened):
|
||||||
|
self.show_windows[name] = bool(opened)
|
||||||
|
if exp: render_func()
|
||||||
|
|
||||||
|
def _render_project_settings_hub(self) -> None:
|
||||||
|
with imscope.tab_bar('context_hub_tabs'):
|
||||||
|
with imscope.tab_item('Projects') as (exp, _):
|
||||||
|
if exp: self._render_projects_panel()
|
||||||
|
with imscope.tab_item('Paths') as (exp, _):
|
||||||
|
if exp: self._render_paths_panel()
|
||||||
|
|
||||||
|
def _render_ai_settings_hub(self) -> None:
|
||||||
|
self._render_persona_selector_panel()
|
||||||
|
if imgui.collapsing_header("Provider & Model"): self._render_provider_panel()
|
||||||
|
if imgui.collapsing_header("System Prompts"): self._render_system_prompts_panel()
|
||||||
|
if imgui.collapsing_header("RAG Settings"): self._render_rag_panel()
|
||||||
|
self._render_agent_tools_panel()
|
||||||
|
|
||||||
|
def _render_discussion_hub(self) -> None:
|
||||||
|
with imscope.tab_bar("discussion_hub_tabs"):
|
||||||
|
with imscope.tab_item("Discussion") as (exp, _):
|
||||||
|
if exp: self._render_discussion_tab()
|
||||||
|
with imscope.tab_item("Context Composition") as (exp, _):
|
||||||
|
if exp: self._render_context_composition_panel()
|
||||||
|
with imscope.tab_item("Snapshot") as (exp, _):
|
||||||
|
if exp: self._render_snapshot_tab()
|
||||||
|
with imscope.tab_item("Takes") as (exp, _):
|
||||||
|
if exp: self._render_takes_panel()
|
||||||
|
|
||||||
|
def _render_operations_hub(self) -> None:
|
||||||
|
imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4))
|
||||||
|
ch1, self.ui_separate_tool_calls_panel = imgui.checkbox("Pop Out Tool Calls", self.ui_separate_tool_calls_panel)
|
||||||
|
if ch1: self.show_windows["Tool Calls"] = self.ui_separate_tool_calls_panel
|
||||||
|
imgui.same_line()
|
||||||
|
ch2, self.ui_separate_usage_analytics = imgui.checkbox("Pop Out Usage Analytics", self.ui_separate_usage_analytics)
|
||||||
|
if ch2: self.show_windows["Usage Analytics"] = self.ui_separate_usage_analytics
|
||||||
|
imgui.same_line()
|
||||||
|
ch3, self.ui_separate_external_tools = imgui.checkbox('Pop Out External Tools', self.ui_separate_external_tools)
|
||||||
|
if ch3: self.show_windows['External Tools'] = self.ui_separate_external_tools
|
||||||
|
imgui.pop_style_var()
|
||||||
|
show_tc_tab, show_usage_tab = not self.ui_separate_tool_calls_panel, not self.ui_separate_usage_analytics
|
||||||
|
with imscope.tab_bar("ops_tabs"):
|
||||||
|
with imscope.tab_item("Comms History") as (exp, _):
|
||||||
|
if exp: self._render_comms_history_panel()
|
||||||
|
if show_tc_tab:
|
||||||
|
with imscope.tab_item("Tool Calls") as (exp, _):
|
||||||
|
if exp: self._render_tool_calls_panel()
|
||||||
|
if show_usage_tab:
|
||||||
|
with imscope.tab_item("Usage Analytics") as (exp, _):
|
||||||
|
if exp: self._render_usage_analytics_panel()
|
||||||
|
if not self.ui_separate_external_tools:
|
||||||
|
with imscope.tab_item("External Tools") as (exp, _):
|
||||||
|
if exp:
|
||||||
|
self._render_external_tools_panel()
|
||||||
|
imgui.separator(); imgui.text("")
|
||||||
|
try: self._render_external_editor_panel()
|
||||||
|
except Exception as e: imgui.text_colored(vec4(1, 0.3, 0.3, 1), f"Error: {str(e)}")
|
||||||
|
with imscope.tab_item("Workspace Layouts") as (exp, _):
|
||||||
|
if exp:
|
||||||
|
imgui.text("Experimental: Auto-switch layout by Tier")
|
||||||
|
ch, self.controller.ui_auto_switch_layout = imgui.checkbox("Enable Auto-Switch", self.controller.ui_auto_switch_layout)
|
||||||
|
if self.controller.ui_auto_switch_layout:
|
||||||
|
imgui.separator(); imgui.text("Tier Bindings (select profile for each tier)")
|
||||||
|
profiles = [""] + [p.name for p in self.controller.workspace_profiles.values()]
|
||||||
|
for t in ["Tier 1", "Tier 2", "Tier 3", "Tier 4"]:
|
||||||
|
curr = self.controller.ui_tier_layout_bindings.get(t, ""); idx = profiles.index(curr) if curr in profiles else 0
|
||||||
|
ch_combo, new_idx = imgui.combo(t, idx, profiles)
|
||||||
|
if ch_combo: self.controller.ui_tier_layout_bindings[t] = profiles[new_idx]
|
||||||
|
|
||||||
|
def _render_thinking_indicator(self) -> None:
|
||||||
|
is_thinking = self.ai_status in ['sending...', 'streaming...', 'running powershell...']
|
||||||
|
if is_thinking:
|
||||||
|
val = math.sin(time.time() * 10 * math.pi)
|
||||||
|
alpha = 1.0 if val > 0 else 0.0
|
||||||
|
c = vec4(255, 50, 50, alpha) if theme.is_nerv_active() else vec4(255, 100, 100, alpha)
|
||||||
|
imgui.text_colored(c, "THINKING..."); imgui.same_line()
|
||||||
|
|
||||||
|
def _render_prior_session_view(self) -> None:
|
||||||
|
imgui.push_style_color(imgui.Col_.child_bg, vec4(50, 40, 20))
|
||||||
|
if imgui.button("Exit Prior Session"): self.controller.cb_exit_prior_session(); self._comms_log_dirty = True
|
||||||
|
imgui.separator()
|
||||||
|
with imscope.child("prior_scroll"):
|
||||||
|
clipper = imgui.ListClipper(); clipper.begin(len(self.prior_disc_entries))
|
||||||
|
while clipper.step():
|
||||||
|
for idx in range(clipper.display_start, clipper.display_end):
|
||||||
|
entry = self.prior_disc_entries[idx]; with imscope.id(f"prior_disc_{idx}"):
|
||||||
|
collapsed = entry.get("collapsed", False)
|
||||||
|
if imgui.button("+" if collapsed else "-"): entry["collapsed"] = not collapsed
|
||||||
|
imgui.same_line(); role, ts = entry.get("role", "??"), entry.get("ts", "")
|
||||||
|
imgui.text_colored(C_LBL, f"[{role}]")
|
||||||
|
if ts: imgui.same_line(); imgui.text_colored(vec4(160, 160, 160), str(ts))
|
||||||
|
content = entry.get("content", "")
|
||||||
|
if collapsed:
|
||||||
|
imgui.same_line(); preview = content.replace("\n", " ")[:80]
|
||||||
|
if len(content) > 80: preview += "..."
|
||||||
|
imgui.text_colored(vec4(180, 180, 180), preview)
|
||||||
|
else:
|
||||||
|
is_nerv = theme.is_nerv_active()
|
||||||
|
if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80))
|
||||||
|
markdown_helper.render(content, context_id=f'prior_disc_{idx}')
|
||||||
|
if is_nerv: imgui.pop_style_color()
|
||||||
|
imgui.separator()
|
||||||
|
imgui.pop_style_color()
|
||||||
|
|
||||||
|
def _render_discussion_selector(self) -> None:
|
||||||
|
if not imgui.collapsing_header("Discussions", imgui.TreeNodeFlags_.default_open): return
|
||||||
|
names = self._get_discussion_names(); grouped = {}
|
||||||
|
for name in names:
|
||||||
|
base = name.split("_take_")[0]; grouped.setdefault(base, []).append(name)
|
||||||
|
active_base = self.active_discussion.split("_take_")[0]
|
||||||
|
if active_base not in grouped: active_base = names[0] if names else ""
|
||||||
|
base_names = sorted(grouped.keys())
|
||||||
|
if imgui.begin_combo("##disc_sel", active_base):
|
||||||
|
for bname in base_names:
|
||||||
|
is_selected = (bname == active_base)
|
||||||
|
if imgui.selectable(bname, is_selected)[0]:
|
||||||
|
target = bname if bname in names else grouped[bname][0]
|
||||||
|
if target != self.active_discussion: self._switch_discussion(target)
|
||||||
|
if is_selected: imgui.set_item_default_focus()
|
||||||
|
imgui.end_combo()
|
||||||
|
active_base = self.active_discussion.split("_take_")[0]; current_takes = grouped.get(active_base, [])
|
||||||
|
if imgui.begin_tab_bar("discussion_takes_tabs"):
|
||||||
|
for take_name in current_takes:
|
||||||
|
label = "Original" if take_name == active_base else take_name.replace(f"{active_base}_", "").replace("_", " ").title()
|
||||||
|
flags = imgui.TabItemFlags_.set_selected if take_name == self.active_discussion else 0
|
||||||
|
with imscope.tab_item(f"{label}###{take_name}", flags) as (exp, _):
|
||||||
|
if exp and take_name != self.active_discussion: self._switch_discussion(take_name)
|
||||||
|
with imscope.tab_item("Synthesis###Synthesis") as (exp, _):
|
||||||
|
if exp: self._render_synthesis_panel()
|
||||||
|
imgui.end_tab_bar()
|
||||||
|
if "_take_" in self.active_discussion:
|
||||||
|
if imgui.button("Promote Take"):
|
||||||
|
base_name = self.active_discussion.split("_take_")[0]; new_name = f"{base_name}_promoted"; counter = 1
|
||||||
|
while new_name in names: new_name = f"{base_name}_promoted_{counter}"; counter += 1
|
||||||
|
project_manager.promote_take(self.project, self.active_discussion, new_name); self._switch_discussion(new_name)
|
||||||
|
imgui.same_line()
|
||||||
|
if self.active_track:
|
||||||
|
imgui.same_line(); ch, self._track_discussion_active = imgui.checkbox("Track Discussion", self._track_discussion_active)
|
||||||
|
if ch:
|
||||||
|
if self._track_discussion_active:
|
||||||
|
self._flush_disc_entries_to_project()
|
||||||
|
history_strings = project_manager.load_track_history(self.active_track.id, self.active_project_root)
|
||||||
|
with self._disc_entries_lock: self.disc_entries = models.parse_history_entries(history_strings, self.disc_roles)
|
||||||
|
self.ai_status = f"track discussion: {self.active_track.id}"
|
||||||
|
else: self._flush_disc_entries_to_project(); self._switch_discussion(self.active_discussion); self.ai_status = "track discussion disabled"
|
||||||
|
self._render_discussion_metadata()
|
||||||
|
|
||||||
|
def _render_discussion_metadata(self) -> None:
|
||||||
|
disc_data = self.project.get("discussion", {}).get("discussions", {}).get(self.active_discussion, {})
|
||||||
|
git_commit, last_updated = disc_data.get("git_commit", ""), disc_data.get("last_updated", "")
|
||||||
|
imgui.text_colored(C_LBL, "commit:"); imgui.same_line()
|
||||||
|
self._render_selectable_label('git_commit_val', git_commit[:12] if git_commit else '(none)', width=100, color=(C_IN if git_commit else C_LBL))
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Update Commit"):
|
||||||
|
if self.ui_project_git_dir:
|
||||||
|
cmt = project_manager.get_git_commit(self.ui_project_git_dir)
|
||||||
|
if cmt: disc_data["git_commit"], disc_data["last_updated"], self.ai_status = cmt, project_manager.now_ts(), f"commit: {cmt[:12]}"
|
||||||
|
imgui.text_colored(C_LBL, "updated:"); imgui.same_line(); imgui.text_colored(C_SUB, last_updated if last_updated else "(never)")
|
||||||
|
ch, self.ui_disc_new_name_input = imgui.input_text("##new_disc", self.ui_disc_new_name_input); imgui.same_line()
|
||||||
|
if imgui.button("Create"):
|
||||||
|
nm = self.ui_disc_new_name_input.strip()
|
||||||
|
if nm: self._create_discussion(nm); self.ui_disc_new_name_input = ""
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Rename"):
|
||||||
|
nm = self.ui_disc_new_name_input.strip()
|
||||||
|
if nm: self._rename_discussion(self.active_discussion, nm); self.ui_disc_new_name_input = ""
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Delete"): self._delete_discussion(self.active_discussion)
|
||||||
|
|
||||||
|
def _render_discussion_entry_controls(self) -> None:
|
||||||
|
if imgui.button("+ Entry"): self.disc_entries.append({"role": self.disc_roles[0] if self.disc_roles else "User", "content": "", "collapsed": True, "ts": project_manager.now_ts()})
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("-All"):
|
||||||
|
for e in self.disc_entries: e["collapsed"] = True
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("+All"):
|
||||||
|
for e in self.disc_entries: e["collapsed"] = False
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Clear All"): self.disc_entries.clear()
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Save"): self._flush_to_project(); self._flush_to_config(); models.save_config(self.config); self.ai_status = "discussion saved"
|
||||||
|
_, self.ui_auto_add_history = imgui.checkbox("Auto-add message & response to history", self.ui_auto_add_history)
|
||||||
|
imgui.text("Keep Pairs:"); imgui.same_line(); imgui.set_next_item_width(80)
|
||||||
|
ch, self.ui_disc_truncate_pairs = imgui.input_int("##trunc_pairs", self.ui_disc_truncate_pairs, 1)
|
||||||
|
if self.ui_disc_truncate_pairs < 1: self.ui_disc_truncate_pairs = 1
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Truncate"):
|
||||||
|
with self._disc_entries_lock: self.disc_entries = truncate_entries(self.disc_entries, self.ui_disc_truncate_pairs)
|
||||||
|
self.ai_status = f"history truncated to {self.ui_disc_truncate_pairs} pairs"
|
||||||
|
|
||||||
|
def _render_discussion_roles(self) -> None:
|
||||||
|
if imgui.collapsing_header("Roles"):
|
||||||
|
with imscope.child("roles_scroll", size_y=100, flags=True):
|
||||||
|
for i, r in enumerate(list(self.disc_roles)):
|
||||||
|
with imscope.id(f"role_{i}"):
|
||||||
|
if imgui.button("X"): self.disc_roles.pop(i); break
|
||||||
|
imgui.same_line(); imgui.text(r)
|
||||||
|
ch, self.ui_disc_new_role_input = imgui.input_text("##new_role", self.ui_disc_new_role_input); imgui.same_line()
|
||||||
|
if imgui.button("Add"):
|
||||||
|
r = self.ui_disc_new_role_input.strip()
|
||||||
|
if r and r not in self.disc_roles: self.disc_roles.append(r); self.ui_disc_new_role_input = ""
|
||||||
|
|
||||||
|
def _render_discussion_entries(self) -> None:
|
||||||
|
with imscope.child("disc_scroll"):
|
||||||
|
display_entries = self.disc_entries
|
||||||
|
if self.ui_focus_agent:
|
||||||
|
tier_usage = self.mma_tier_usage.get(self.ui_focus_agent)
|
||||||
|
if tier_usage:
|
||||||
|
persona_name = tier_usage.get("persona")
|
||||||
|
if persona_name: display_entries = [e for e in self.disc_entries if e.get("role") == persona_name or e.get("role") == "User"]
|
||||||
|
clipper = imgui.ListClipper(); clipper.begin(len(display_entries))
|
||||||
|
while clipper.step():
|
||||||
|
for i in range(clipper.display_start, clipper.display_end):
|
||||||
|
self._render_discussion_entry(display_entries[i], i)
|
||||||
|
if self._scroll_disc_to_bottom: imgui.set_scroll_here_y(1.0); self._scroll_disc_to_bottom = False
|
||||||
|
|
||||||
|
def _render_discussion_entry(self, entry: dict, index: int) -> None:
|
||||||
|
with imscope.id(f"disc_{index}"):
|
||||||
|
collapsed, read_mode = entry.get("collapsed", False), entry.get("read_mode", False)
|
||||||
|
if imgui.button("+" if collapsed else "-"): entry["collapsed"] = not collapsed
|
||||||
|
imgui.same_line(); self._render_text_viewer(f"Entry #{index+1}", entry["content"]); imgui.same_line(); imgui.set_next_item_width(120)
|
||||||
|
if imgui.begin_combo("##role", entry["role"]):
|
||||||
|
for r in self.disc_roles:
|
||||||
|
if imgui.selectable(r, r == entry["role"])[0]: entry["role"] = r
|
||||||
|
imgui.end_combo()
|
||||||
|
if not collapsed:
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("[Edit]" if read_mode else "[Read]"): entry["read_mode"] = not read_mode
|
||||||
|
ts_str = entry.get("ts", "")
|
||||||
|
if ts_str:
|
||||||
|
imgui.same_line(); imgui.text_colored(vec4(120, 120, 100), str(ts_str)); e_dt = project_manager.parse_ts(ts_str)
|
||||||
|
if e_dt:
|
||||||
|
e_unix, next_unix = e_dt.timestamp(), float('inf')
|
||||||
|
if index + 1 < len(self.disc_entries):
|
||||||
|
n_ts = self.disc_entries[index+1].get("ts", ""); n_dt = project_manager.parse_ts(n_ts)
|
||||||
|
if n_dt: next_unix = n_dt.timestamp()
|
||||||
|
injected = [f for f in self.files if hasattr(f, 'injected_at') and f.injected_at and e_unix <= f.injected_at < next_unix]
|
||||||
|
if injected:
|
||||||
|
imgui.same_line(); imgui.text_colored(vec4(100, 255, 100), f"[{len(injected)}+]")
|
||||||
|
if imgui.is_item_hovered(): imgui.set_tooltip("Files injected at this point:\n" + "\n".join([f.path for f in injected]))
|
||||||
|
if collapsed:
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Ins"): self.disc_entries.insert(index, {"role": "User", "content": "", "collapsed": True, "ts": project_manager.now_ts()})
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Del"): self.disc_entries.pop(index); return
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Branch"): self._branch_discussion(index)
|
||||||
|
imgui.same_line(); preview = entry["content"].replace("\n", " ")[:60]
|
||||||
|
if len(entry["content"]) > 60: preview += "..."
|
||||||
|
if not preview.strip() and entry.get("thinking_segments"):
|
||||||
|
preview = entry["thinking_segments"][0]["content"].replace("\n", " ")[:60]
|
||||||
|
if len(entry["thinking_segments"][0]["content"]) > 60: preview += "..."
|
||||||
|
imgui.text_colored(vec4(160, 160, 150), preview)
|
||||||
|
if not collapsed:
|
||||||
|
thinking_segments, has_content = entry.get("thinking_segments", []), bool(entry.get("content", "").strip())
|
||||||
|
if thinking_segments: self._render_thinking_trace(thinking_segments, index, is_standalone=not has_content)
|
||||||
|
if read_mode: self._render_discussion_entry_read_mode(entry, index)
|
||||||
|
else:
|
||||||
|
if not (bool(thinking_segments) and not has_content): ch, entry["content"] = imgui.input_text_multiline("##content", entry["content"], imgui.ImVec2(-1, 150))
|
||||||
|
imgui.separator()
|
||||||
|
|
||||||
|
def _render_discussion_entry_read_mode(self, entry: dict, index: int) -> None:
|
||||||
|
content = entry["content"]
|
||||||
|
if not content.strip(): return
|
||||||
|
if '## Retrieved Context' in content:
|
||||||
|
rag_match = re.search(r'## Retrieved Context\n\n([\s\S]*?)(?=\n\n#|\Z)', content)
|
||||||
|
if rag_match:
|
||||||
|
rag_section = rag_match.group(1)
|
||||||
|
if imgui.collapsing_header('Retrieved Context'):
|
||||||
|
chunks = re.finditer(r'### Chunk (\d+) \(Source: (.*?)\)\n([\s\S]*?)(?=\n### Chunk|\Z)', rag_section)
|
||||||
|
for chunk_match in chunks:
|
||||||
|
idx, path, chunk_content = chunk_match.group(1), chunk_match.group(2), chunk_match.group(3)
|
||||||
|
if imgui.collapsing_header(f'Chunk {idx}: {path}'):
|
||||||
|
if imgui.button(f'[Source]##rag_{index}_{idx}'):
|
||||||
|
res = mcp_client.read_file(path)
|
||||||
|
if res: self.text_viewer_title, self.text_viewer_content, self.text_viewer_type, self.show_text_viewer = path, res, (Path(path).suffix.lstrip('.') if Path(path).suffix else 'text'), True
|
||||||
|
imgui.text_unformatted(chunk_content)
|
||||||
|
content = content[:rag_match.start()] + content[rag_match.end():]
|
||||||
|
pattern = re.compile(r"\[Definition: (.*?) from (.*?) \(line (\d+)\)\](\s+```[\s\S]*?```)?")
|
||||||
|
matches, is_nerv = list(pattern.finditer(content)), theme.is_nerv_active()
|
||||||
|
if not matches:
|
||||||
|
if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80))
|
||||||
|
markdown_helper.render(content, context_id=f'disc_{index}')
|
||||||
|
if is_nerv: imgui.pop_style_color()
|
||||||
|
else:
|
||||||
|
with imscope.child(f"read_content_{index}", size_y=150, flags=True):
|
||||||
|
if self.ui_word_wrap: imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
|
||||||
|
last_idx = 0
|
||||||
|
for m_idx, match in enumerate(matches):
|
||||||
|
before = content[last_idx:match.start()]
|
||||||
|
if before:
|
||||||
|
if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80))
|
||||||
|
markdown_helper.render(before, context_id=f'disc_{index}_b_{m_idx}')
|
||||||
|
if is_nerv: imgui.pop_style_color()
|
||||||
|
header_text, path, code_block = match.group(0).split("\n")[0].strip(), match.group(2), match.group(4)
|
||||||
|
if imgui.collapsing_header(header_text):
|
||||||
|
if imgui.button(f"[Source]##{index}_{match.start()}"):
|
||||||
|
res = mcp_client.read_file(path)
|
||||||
|
if res: self.text_viewer_title, self.text_viewer_content, self.text_viewer_type, self.show_text_viewer = path, res, (Path(path).suffix.lstrip('.') if Path(path).suffix else 'text'), True
|
||||||
|
if code_block:
|
||||||
|
if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80))
|
||||||
|
markdown_helper.render(code_block, context_id=f'disc_{index}_c_{m_idx}')
|
||||||
|
if is_nerv: imgui.pop_style_color()
|
||||||
|
last_idx = match.end()
|
||||||
|
after = content[last_idx:]
|
||||||
|
if after:
|
||||||
|
if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80))
|
||||||
|
markdown_helper.render(after, context_id=f'disc_{index}_a')
|
||||||
|
if is_nerv: imgui.pop_style_color()
|
||||||
|
if self.ui_word_wrap: imgui.pop_text_wrap_pos()
|
||||||
|
|
||||||
|
def _render_mma_focus_selector(self) -> None:
|
||||||
|
imgui.text("Focus Agent:"); imgui.same_line()
|
||||||
|
focus_label = self.ui_focus_agent or "All"
|
||||||
|
if imgui.begin_combo("##focus_agent", focus_label, imgui.ComboFlags_.width_fit_preview):
|
||||||
|
if imgui.selectable("All", self.ui_focus_agent is None)[0]: self.ui_focus_agent = None
|
||||||
|
for tier in ["Tier 2", "Tier 3", "Tier 4"]:
|
||||||
|
if imgui.selectable(tier, self.ui_focus_agent == tier)[0]: self.ui_focus_agent = tier
|
||||||
|
imgui.end_combo()
|
||||||
|
imgui.same_line()
|
||||||
|
if self.ui_focus_agent and imgui.button("x##clear_focus"): self.ui_focus_agent = None
|
||||||
|
|
||||||
|
def _render_mma_track_summary(self) -> None:
|
||||||
|
is_nerv = theme.is_nerv_active()
|
||||||
|
track_name = self.active_track.description if self.active_track else "None"
|
||||||
|
if getattr(self, "ui_project_execution_mode", "native") == "beads": track_name = "Beads Graph"
|
||||||
|
track_stats = project_manager.calculate_track_progress(self.active_track.tickets if self.active_track else self.active_tickets)
|
||||||
|
total_cost = sum(cost_tracker.estimate_cost(u.get('model','unknown'), u.get('input',0), u.get('output',0)) for u in self.mma_tier_usage.values())
|
||||||
|
imgui.text("Track:"); imgui.same_line(); imgui.text_colored(C_VAL, track_name); imgui.same_line(); imgui.text(" | Status:"); imgui.same_line()
|
||||||
|
if self.mma_status == "paused":
|
||||||
|
imgui.text_colored(vec4(255, 152, 48) if is_nerv else imgui.ImVec4(1, 0.5, 0, 1), "PIPELINE PAUSED"); imgui.same_line()
|
||||||
|
status_col = imgui.ImVec4(1, 1, 1, 1)
|
||||||
|
if self.mma_status == "idle": status_col = imgui.ImVec4(0.7, 0.7, 0.7, 1)
|
||||||
|
elif self.mma_status == "running": status_col = vec4(80, 255, 80) if is_nerv else imgui.ImVec4(1, 1, 0, 1)
|
||||||
|
elif self.mma_status == "done": status_col = imgui.ImVec4(0, 1, 0, 1)
|
||||||
|
elif self.mma_status == "error": status_col = vec4(255, 72, 64) if is_nerv else imgui.ImVec4(1, 0, 0, 1)
|
||||||
|
elif self.mma_status == "paused": status_col = imgui.ImVec4(1, 0.5, 0, 1)
|
||||||
|
imgui.text_colored(status_col, self.mma_status.upper()); imgui.same_line(); imgui.text(" | Cost:"); imgui.same_line(); imgui.text_colored(imgui.ImVec4(0, 1, 0, 1), f"${total_cost:,.4f}")
|
||||||
|
perc = track_stats["percentage"] / 100.0
|
||||||
|
p_color = imgui.ImVec4(1, 0, 0, 1) if track_stats["percentage"] < 33 else (imgui.ImVec4(1, 1, 0, 1) if track_stats["percentage"] < 66 else imgui.ImVec4(0, 1, 0, 1))
|
||||||
|
imgui.push_style_color(imgui.Col_.plot_histogram, p_color); imgui.progress_bar(perc, imgui.ImVec2(-1, 0), f"{track_stats['percentage']:.1f}%"); imgui.pop_style_color()
|
||||||
|
if imgui.begin_table("ticket_stats_breakdown", 4):
|
||||||
|
for lbl, val in [("Completed:", track_stats["completed"]), ("In Progress:", track_stats["in_progress"]), ("Blocked:", track_stats["blocked"]), ("Todo:", track_stats["todo"])]:
|
||||||
|
imgui.table_next_column(); imgui.text_colored(C_LBL, lbl); imgui.same_line(); imgui.text_colored(C_VAL, str(val))
|
||||||
|
imgui.end_table()
|
||||||
|
if self.active_track:
|
||||||
|
remaining = track_stats["total"] - track_stats["completed"]
|
||||||
|
eta_mins = (self._avg_ticket_time * remaining) / 60.0
|
||||||
|
imgui.text_colored(C_LBL, "ETA:"); imgui.same_line(); imgui.text_colored(C_VAL, f"~{int(eta_mins)}m ({remaining} tickets remaining)")
|
||||||
|
|
||||||
|
def _render_mma_epic_planner(self) -> None:
|
||||||
|
imgui.text_colored(C_LBL, 'Epic Planning (Tier 1)')
|
||||||
|
_, self.ui_epic_input = imgui.input_text_multiline('##epic_input', self.ui_epic_input, imgui.ImVec2(-1, 80))
|
||||||
|
if imgui.button('Plan Epic (Tier 1)', imgui.ImVec2(-1, 0)): self._cb_plan_epic()
|
||||||
|
|
||||||
|
def _render_mma_conductor_setup(self) -> None:
|
||||||
|
if imgui.button("Run Setup Scan"): self._cb_run_conductor_setup()
|
||||||
|
if self.ui_conductor_setup_summary: imgui.input_text_multiline("##setup_summary", self.ui_conductor_setup_summary, imgui.ImVec2(-1, 120), imgui.InputTextFlags_.read_only)
|
||||||
|
|
||||||
|
def _render_mma_track_browser(self) -> None:
|
||||||
|
imgui.text("Track Browser")
|
||||||
|
if imgui.begin_table("mma_tracks_table", 4, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable):
|
||||||
|
imgui.table_setup_column("Title"); imgui.table_setup_column("Status"); imgui.table_setup_column("Progress"); imgui.table_setup_column("Actions"); imgui.table_headers_row()
|
||||||
|
for track in self.tracks:
|
||||||
|
imgui.table_next_row(); imgui.table_next_column(); imgui.text(track.get("title", "Untitled")); imgui.table_next_column()
|
||||||
|
status = track.get("status", "unknown").lower()
|
||||||
|
c = imgui.ImVec4(0.7, 0.7, 0.7, 1) if status == "new" else (vec4(80, 255, 80) if status == "active" and theme.is_nerv_active() else (imgui.ImVec4(1, 1, 0, 1) if status == "active" else (imgui.ImVec4(0, 1, 0, 1) if status == "done" else (imgui.ImVec4(1, 0, 0, 1) if status == "blocked" else imgui.ImVec4(1, 1, 1, 1)))))
|
||||||
|
imgui.text_colored(c, status.upper()); imgui.table_next_column()
|
||||||
|
prog = track.get("progress", 0.0)
|
||||||
|
p_c = imgui.ImVec4(1, 0, 0, 1) if prog < 0.33 else (imgui.ImVec4(1, 1, 0, 1) if prog < 0.66 else imgui.ImVec4(0, 1, 0, 1))
|
||||||
|
imgui.push_style_color(imgui.Col_.plot_histogram, p_c); imgui.progress_bar(prog, imgui.ImVec2(-1, 0), f"{int(prog*100)}%"); imgui.pop_style_color(); imgui.table_next_column()
|
||||||
|
if imgui.button(f"Load##{track.get('id')}"): self._cb_load_track(str(track.get("id") or ""))
|
||||||
|
imgui.end_table()
|
||||||
|
imgui.text("Create New Track")
|
||||||
|
_, self.ui_new_track_name = imgui.input_text("Name##new_track", self.ui_new_track_name)
|
||||||
|
_, self.ui_new_track_desc = imgui.input_text_multiline("Description##new_track", self.ui_new_track_desc, imgui.ImVec2(-1, 60))
|
||||||
|
imgui.text("Type:"); imgui.same_line()
|
||||||
|
if imgui.begin_combo("##track_type", self.ui_new_track_type):
|
||||||
|
for ttype in ["feature", "chore", "fix"]:
|
||||||
|
if imgui.selectable(ttype, self.ui_new_track_type == ttype)[0]: self.ui_new_track_type = ttype
|
||||||
|
imgui.end_combo()
|
||||||
|
if imgui.button("Create Track"):
|
||||||
|
self._cb_create_track(self.ui_new_track_name, self.ui_new_track_desc, self.ui_new_track_type)
|
||||||
|
self.ui_new_track_name = ""; self.ui_new_track_desc = ""
|
||||||
|
|
||||||
|
def _render_mma_global_controls(self) -> None:
|
||||||
|
changed, self.mma_step_mode = imgui.checkbox("Step Mode (HITL)", self.mma_step_mode)
|
||||||
|
imgui.same_line(); imgui.text(f"Status: {self.mma_status.upper()}")
|
||||||
|
if self.controller and hasattr(self.controller, 'engine') and self.controller.engine and hasattr(self.controller.engine, '_pause_event'):
|
||||||
|
imgui.same_line()
|
||||||
|
is_paused = self.controller.engine._pause_event.is_set()
|
||||||
|
if imgui.button("Resume" if is_paused else "Pause"):
|
||||||
|
if is_paused: self.controller.engine.resume()
|
||||||
|
else: self.controller.engine.pause()
|
||||||
|
if self.active_tier:
|
||||||
|
imgui.same_line(); imgui.text_colored(C_VAL, f"| Active: {self.active_tier}")
|
||||||
|
any_pending = len(self._pending_mma_spawns) > 0 or len(self._pending_mma_approvals) > 0 or self._pending_ask_dialog
|
||||||
|
if any_pending:
|
||||||
|
alpha = abs(math.sin(time.time() * 5))
|
||||||
|
c = vec4(255, 72, 64, alpha) if theme.is_nerv_active() else imgui.ImVec4(1, 0.3, 0.3, alpha)
|
||||||
|
imgui.same_line(); imgui.text_colored(c, " APPROVAL PENDING"); imgui.same_line()
|
||||||
|
if imgui.button("Go to Approval"): pass
|
||||||
|
|
||||||
|
def _render_mma_usage_section(self) -> None:
|
||||||
|
imgui.text("Tier Usage (Tokens & Cost)")
|
||||||
|
if imgui.begin_table("mma_usage", 5, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg):
|
||||||
|
imgui.table_setup_column("Tier"); imgui.table_setup_column("Model"); imgui.table_setup_column("Input"); imgui.table_setup_column("Output"); imgui.table_setup_column("Est. Cost"); imgui.table_headers_row()
|
||||||
|
total_cost = 0.0
|
||||||
|
for tier, stats in self.mma_tier_usage.items():
|
||||||
|
imgui.table_next_row(); imgui.table_next_column(); imgui.text(tier); imgui.table_next_column(); model = stats.get('model', 'unknown'); imgui.text(model); imgui.table_next_column(); in_t = stats.get('input', 0); imgui.text(f"{in_t:,}"); imgui.table_next_column(); out_t = stats.get('output', 0); imgui.text(f"{out_t:,}"); imgui.table_next_column(); cost = cost_tracker.estimate_cost(model, in_t, out_t); total_cost += cost; imgui.text(f"${cost:,.4f}")
|
||||||
|
imgui.table_next_row(); imgui.table_set_bg_color(imgui.TableBgTarget_.row_bg0, imgui.get_color_u32(imgui.Col_.plot_lines_hovered)); imgui.table_next_column(); imgui.text("TOTAL"); imgui.table_next_column(); imgui.text(""); imgui.table_next_column(); imgui.text(""); imgui.table_next_column(); imgui.text(""); imgui.table_next_column(); imgui.text(f"${total_cost:,.4f}"); imgui.end_table()
|
||||||
|
if imgui.collapsing_header("Tier Model Config"):
|
||||||
|
for tier in self.mma_tier_usage.keys():
|
||||||
|
imgui.text(f"{tier}:"); imgui.same_line(); curr_model, curr_prov = self.mma_tier_usage[tier].get("model", "unknown"), self.mma_tier_usage[tier].get("provider", "gemini")
|
||||||
|
with imscope.id(f"tier_cfg_{tier}"):
|
||||||
|
imgui.push_item_width(80)
|
||||||
|
if imgui.begin_combo("##prov", curr_prov):
|
||||||
|
for p in models.PROVIDERS:
|
||||||
|
if imgui.selectable(p, p == curr_prov)[0]:
|
||||||
|
self.mma_tier_usage[tier]["provider"] = p
|
||||||
|
models_list = self.controller.all_available_models.get(p, [])
|
||||||
|
if models_list: self.mma_tier_usage[tier]["model"] = models_list[0]
|
||||||
|
imgui.end_combo()
|
||||||
|
imgui.pop_item_width(); imgui.same_line(); imgui.push_item_width(150)
|
||||||
|
models_list = self.controller.all_available_models.get(curr_prov, [])
|
||||||
|
if imgui.begin_combo("##model", curr_model):
|
||||||
|
for m in models_list:
|
||||||
|
if imgui.selectable(m, curr_model == m)[0]: self.mma_tier_usage[tier]["model"] = m
|
||||||
|
imgui.end_combo()
|
||||||
|
imgui.pop_item_width(); imgui.same_line(); imgui.push_item_width(-1)
|
||||||
|
curr_preset = self.mma_tier_usage[tier].get("tool_preset") or "None"
|
||||||
|
p_names = ["None"] + sorted(self.controller.tool_presets.keys())
|
||||||
|
if imgui.begin_combo("##preset", curr_preset):
|
||||||
|
for pn in p_names:
|
||||||
|
if imgui.selectable(pn, curr_preset == pn)[0]: self.mma_tier_usage[tier]["tool_preset"] = None if pn == "None" else pn
|
||||||
|
imgui.end_combo()
|
||||||
|
imgui.pop_item_width(); imgui.same_line(); imgui.push_item_width(150)
|
||||||
|
curr_pers = self.mma_tier_usage[tier].get("persona") or "None"
|
||||||
|
personas = getattr(self.controller, 'personas', {})
|
||||||
|
pers_opts = ["None"] + sorted(personas.keys())
|
||||||
|
if imgui.begin_combo("##persona", curr_pers):
|
||||||
|
for pern in pers_opts:
|
||||||
|
if imgui.selectable(pern, curr_pers == pern)[0]: self.mma_tier_usage[tier]["persona"] = None if pern == "None" else pern
|
||||||
|
imgui.end_combo()
|
||||||
|
imgui.pop_item_width()
|
||||||
|
|
||||||
|
def _render_mma_ticket_editor(self) -> None:
|
||||||
|
imgui.separator(); imgui.text_colored(C_VAL, f"Editing: {self.ui_selected_ticket_id}")
|
||||||
|
ticket = next((t for t in self.active_tickets if str(t.get('id', '')) == self.ui_selected_ticket_id), None)
|
||||||
|
if ticket:
|
||||||
|
imgui.text(f"Status: {ticket.get('status', 'todo')}"); prio = ticket.get('priority', 'medium')
|
||||||
|
imgui.text("Priority:"); imgui.same_line()
|
||||||
|
if imgui.begin_combo(f"##edit_prio_{ticket.get('id')}", prio):
|
||||||
|
for p_opt in ['high', 'medium', 'low']:
|
||||||
|
if imgui.selectable(p_opt, p_opt == prio)[0]: ticket['priority'] = p_opt; self._push_mma_state_update()
|
||||||
|
imgui.end_combo()
|
||||||
|
imgui.text(f"Target: {ticket.get('target_file', '')}"); imgui.text(f"Depends on: {', '.join(ticket.get('depends_on', []))}")
|
||||||
|
personas = getattr(self.controller, 'personas', {}); curr_pers = ticket.get('persona_id', '')
|
||||||
|
imgui.text("Persona Override:"); imgui.same_line()
|
||||||
|
pers_opts = ["None"] + sorted(personas.keys()); curr_idx = pers_opts.index(curr_pers) + 1 if curr_pers in pers_opts else 0
|
||||||
|
_, curr_idx = imgui.combo(f"##ticket_persona_{ticket.get('id')}", curr_idx, pers_opts)
|
||||||
|
ticket['persona_id'] = None if curr_idx == 0 or pers_opts[curr_idx] == "None" else pers_opts[curr_idx]
|
||||||
|
if imgui.button(f"Mark Complete##{self.ui_selected_ticket_id}"): ticket['status'] = 'done'; self._push_mma_state_update()
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button(f"Delete##{self.ui_selected_ticket_id}"): self.active_tickets = [t for t in self.active_tickets if str(t.get('id', '')) != self.ui_selected_ticket_id]; self.ui_selected_ticket_id = None; self._push_mma_state_update()
|
||||||
|
|
||||||
|
def _render_mma_agent_streams(self) -> None:
|
||||||
|
imgui.text("Agent Streams")
|
||||||
|
if imgui.begin_tab_bar("mma_streams_tabs"):
|
||||||
|
for tier, label, sep_flag_attr in [("Tier 1", "Tier 1", "ui_separate_tier1"), ("Tier 2", "Tier 2 (Tech Lead)", "ui_separate_tier2"), ("Tier 3", None, "ui_separate_tier3"), ("Tier 4", "Tier 4 (QA)", "ui_separate_tier4")]:
|
||||||
|
with imscope.tab_item(tier) as (exp, _):
|
||||||
|
if exp:
|
||||||
|
sep_val = getattr(self, sep_flag_attr); ch, new_val = imgui.checkbox(f"Pop Out {tier}", sep_val)
|
||||||
|
if ch:
|
||||||
|
setattr(self, sep_flag_attr, new_val)
|
||||||
|
self.show_windows[f"{tier}: Strategy" if tier == "Tier 1" else (f"{tier}: Tech Lead" if tier == "Tier 2" else (f"{tier}: Workers" if tier == "Tier 3" else f"{tier}: QA"))] = new_val
|
||||||
|
if not new_val: self._render_tier_stream_panel(tier, label)
|
||||||
|
else: imgui.text_disabled(f"{tier} stream is detached.")
|
||||||
|
if getattr(self, "ui_project_execution_mode", "native") == "beads":
|
||||||
|
with imscope.tab_item("Beads") as (exp, _):
|
||||||
|
if exp: self._render_beads_tab()
|
||||||
|
imgui.end_tab_bar()
|
||||||
|
|
||||||
if imgui.begin_popup_modal("Approve PowerShell Command", None, imgui.WindowFlags_.always_auto_resize)[0]:
|
if imgui.begin_popup_modal("Approve PowerShell Command", None, imgui.WindowFlags_.always_auto_resize)[0]:
|
||||||
if not dlg:
|
if not dlg:
|
||||||
imgui.close_current_popup()
|
imgui.close_current_popup()
|
||||||
@@ -4693,429 +5183,35 @@ def hello():
|
|||||||
imgui.end_table()
|
imgui.end_table()
|
||||||
|
|
||||||
def _render_mma_dashboard(self) -> None:
|
def _render_mma_dashboard(self) -> None:
|
||||||
"""
|
"""Main MMA dashboard interface."""
|
||||||
[C: tests/test_gui_progress.py:test_render_mma_dashboard_progress, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_approval_badge_shown_when_ask_dialog_pending, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_approval_badge_shown_when_mma_approval_pending, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_approval_badge_shown_when_spawn_pending, tests/test_mma_approval_indicators.py:TestMMAApprovalIndicators.test_no_approval_badge_when_idle]
|
|
||||||
"""
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_mma_dashboard")
|
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_mma_dashboard")
|
||||||
|
self._render_mma_focus_selector()
|
||||||
# Focus Agent dropdown
|
|
||||||
imgui.text("Focus Agent:")
|
|
||||||
imgui.same_line()
|
|
||||||
focus_label = self.ui_focus_agent or "All"
|
|
||||||
if imgui.begin_combo("##focus_agent", focus_label, imgui.ComboFlags_.width_fit_preview):
|
|
||||||
if imgui.selectable("All", self.ui_focus_agent is None)[0]:
|
|
||||||
self.ui_focus_agent = None
|
|
||||||
for tier in ["Tier 2", "Tier 3", "Tier 4"]:
|
|
||||||
if imgui.selectable(tier, self.ui_focus_agent == tier)[0]:
|
|
||||||
self.ui_focus_agent = tier
|
|
||||||
imgui.end_combo()
|
|
||||||
imgui.same_line()
|
|
||||||
if self.ui_focus_agent:
|
|
||||||
if imgui.button("x##clear_focus"):
|
|
||||||
self.ui_focus_agent = None
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
|
|
||||||
is_nerv = theme.is_nerv_active()
|
|
||||||
if self.is_viewing_prior_session:
|
if self.is_viewing_prior_session:
|
||||||
c = vec4(255, 200, 100)
|
c = vec4(255, 152, 48) if theme.is_nerv_active() else vec4(255, 200, 100)
|
||||||
if is_nerv: c = vec4(255, 152, 48) # NERV_ORANGE
|
|
||||||
imgui.text_colored(c, "HISTORICAL VIEW - READ ONLY")
|
imgui.text_colored(c, "HISTORICAL VIEW - READ ONLY")
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_mma_dashboard")
|
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_mma_dashboard")
|
||||||
return
|
return
|
||||||
# Task 5.3: Dense Summary Line
|
self._render_mma_track_summary()
|
||||||
track_name = self.active_track.description if self.active_track else "None"
|
|
||||||
if getattr(self, "ui_project_execution_mode", "native") == "beads":
|
|
||||||
track_name = "Beads Graph"
|
|
||||||
track_stats = {"percentage": 0.0, "completed": 0, "total": 0, "in_progress": 0, "blocked": 0, "todo": 0}
|
|
||||||
if self.active_track:
|
|
||||||
track_stats = project_manager.calculate_track_progress(self.active_track.tickets)
|
|
||||||
elif self.active_tickets:
|
|
||||||
track_stats = project_manager.calculate_track_progress(self.active_tickets)
|
|
||||||
total_cost = 0.0
|
|
||||||
for usage in self.mma_tier_usage.values():
|
|
||||||
model = usage.get('model', 'unknown')
|
|
||||||
in_t = usage.get('input', 0)
|
|
||||||
out_t = usage.get('output', 0)
|
|
||||||
total_cost += cost_tracker.estimate_cost(model, in_t, out_t)
|
|
||||||
|
|
||||||
imgui.text("Track:")
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text_colored(C_VAL, track_name)
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text(" | Status:")
|
|
||||||
imgui.same_line()
|
|
||||||
if self.mma_status == "paused":
|
|
||||||
c = imgui.ImVec4(1, 0.5, 0, 1)
|
|
||||||
if is_nerv: c = vec4(255, 152, 48)
|
|
||||||
imgui.text_colored(c, "PIPELINE PAUSED")
|
|
||||||
imgui.same_line()
|
|
||||||
status_col = imgui.ImVec4(1, 1, 1, 1)
|
|
||||||
if self.mma_status == "idle": status_col = imgui.ImVec4(0.7, 0.7, 0.7, 1)
|
|
||||||
elif self.mma_status == "running": status_col = imgui.ImVec4(1, 1, 0, 1)
|
|
||||||
elif self.mma_status == "done": status_col = imgui.ImVec4(0, 1, 0, 1)
|
|
||||||
elif self.mma_status == "error": status_col = imgui.ImVec4(1, 0, 0, 1)
|
|
||||||
elif self.mma_status == "paused": status_col = imgui.ImVec4(1, 0.5, 0, 1)
|
|
||||||
|
|
||||||
if is_nerv:
|
|
||||||
if self.mma_status == "running": status_col = vec4(80, 255, 80) # DATA_GREEN
|
|
||||||
elif self.mma_status == "error": status_col = vec4(255, 72, 64) # ALERT_RED
|
|
||||||
|
|
||||||
imgui.text_colored(status_col, self.mma_status.upper())
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text(" | Cost:")
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text_colored(imgui.ImVec4(0, 1, 0, 1), f"${total_cost:,.4f}")
|
|
||||||
|
|
||||||
# Progress Bar
|
|
||||||
perc = track_stats["percentage"] / 100.0
|
|
||||||
p_color = imgui.ImVec2(0.0, 1.0) # WAIT WRONG TYPE
|
|
||||||
|
|
||||||
p_color = imgui.ImVec4(0.0, 1.0, 0.0, 1.0)
|
|
||||||
if track_stats["percentage"] < 33:
|
|
||||||
p_color = imgui.ImVec4(1.0, 0.0, 0.0, 1.0)
|
|
||||||
elif track_stats["percentage"] < 66:
|
|
||||||
p_color = imgui.ImVec4(1.0, 1.0, 0.0, 1.0)
|
|
||||||
|
|
||||||
imgui.push_style_color(imgui.Col_.plot_histogram, p_color)
|
|
||||||
imgui.progress_bar(perc, imgui.ImVec2(-1, 0), f"{track_stats['percentage']:.1f}%")
|
|
||||||
imgui.pop_style_color()
|
|
||||||
|
|
||||||
# Detailed breakdown
|
|
||||||
if imgui.begin_table("ticket_stats_breakdown", 4):
|
|
||||||
imgui.table_next_column()
|
|
||||||
imgui.text_colored(C_LBL, "Completed:")
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text_colored(C_VAL, str(track_stats["completed"]))
|
|
||||||
|
|
||||||
imgui.table_next_column()
|
|
||||||
imgui.text_colored(C_LBL, "In Progress:")
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text_colored(C_VAL, str(track_stats["in_progress"]))
|
|
||||||
|
|
||||||
imgui.table_next_column()
|
|
||||||
imgui.text_colored(C_LBL, "Blocked:")
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text_colored(C_VAL, str(track_stats["blocked"]))
|
|
||||||
|
|
||||||
imgui.table_next_column()
|
|
||||||
imgui.text_colored(C_LBL, "Todo:")
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text_colored(C_VAL, str(track_stats["todo"]))
|
|
||||||
|
|
||||||
imgui.end_table()
|
|
||||||
|
|
||||||
if self.active_track:
|
|
||||||
remaining = track_stats["total"] - track_stats["completed"]
|
|
||||||
eta_mins = (self._avg_ticket_time * remaining) / 60.0
|
|
||||||
imgui.text_colored(C_LBL, "ETA:")
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text_colored(C_VAL, f"~{int(eta_mins)}m ({remaining} tickets remaining)")
|
|
||||||
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
imgui.text_colored(C_LBL, 'Epic Planning (Tier 1)')
|
self._render_mma_epic_planner()
|
||||||
_, self.ui_epic_input = imgui.input_text_multiline('##epic_input', self.ui_epic_input, imgui.ImVec2(-1, 80))
|
|
||||||
if imgui.button('Plan Epic (Tier 1)', imgui.ImVec2(-1, 0)):
|
|
||||||
self._cb_plan_epic()
|
|
||||||
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
# 0. Conductor Setup
|
if imgui.collapsing_header("Conductor Setup"): self._render_mma_conductor_setup()
|
||||||
if imgui.collapsing_header("Conductor Setup"):
|
|
||||||
if imgui.button("Run Setup Scan"):
|
|
||||||
self._cb_run_conductor_setup()
|
|
||||||
if self.ui_conductor_setup_summary:
|
|
||||||
imgui.input_text_multiline("##setup_summary", self.ui_conductor_setup_summary, imgui.ImVec2(-1, 120), imgui.InputTextFlags_.read_only)
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
# 1. Track Browser
|
self._render_mma_track_browser()
|
||||||
imgui.text("Track Browser")
|
|
||||||
if imgui.begin_table("mma_tracks_table", 4, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable):
|
|
||||||
imgui.table_setup_column("Title")
|
|
||||||
imgui.table_setup_column("Status")
|
|
||||||
imgui.table_setup_column("Progress")
|
|
||||||
imgui.table_setup_column("Actions")
|
|
||||||
imgui.table_headers_row()
|
|
||||||
for track in self.tracks:
|
|
||||||
imgui.table_next_row()
|
|
||||||
imgui.table_next_column()
|
|
||||||
imgui.text(track.get("title", "Untitled"))
|
|
||||||
imgui.table_next_column()
|
|
||||||
status = track.get("status", "unknown").lower()
|
|
||||||
if status == "new":
|
|
||||||
imgui.text_colored(imgui.ImVec4(0.7, 0.7, 0.7, 1.0), "NEW")
|
|
||||||
elif status == "active":
|
|
||||||
c = imgui.ImVec4(1.0, 1.0, 0.0, 1.0)
|
|
||||||
if is_nerv: c = vec4(80, 255, 80)
|
|
||||||
imgui.text_colored(c, "ACTIVE")
|
|
||||||
elif status == "done":
|
|
||||||
imgui.text_colored(imgui.ImVec4(0.0, 1.0, 0.0, 1.0), "DONE")
|
|
||||||
elif status == "blocked":
|
|
||||||
imgui.text_colored(imgui.ImVec4(1.0, 0.0, 0.0, 1.0), "BLOCKED")
|
|
||||||
else:
|
|
||||||
imgui.text(status)
|
|
||||||
imgui.table_next_column()
|
|
||||||
progress = track.get("progress", 0.0)
|
|
||||||
if progress < 0.33:
|
|
||||||
p_color = imgui.ImVec4(1.0, 0.0, 0.0, 1.0)
|
|
||||||
elif progress < 0.66:
|
|
||||||
p_color = imgui.ImVec4(1.0, 1.0, 0.0, 1.0)
|
|
||||||
else:
|
|
||||||
p_color = imgui.ImVec4(0.0, 1.0, 0.0, 1.0)
|
|
||||||
imgui.push_style_color(imgui.Col_.plot_histogram, p_color)
|
|
||||||
imgui.progress_bar(progress, imgui.ImVec2(-1, 0), f"{int(progress*100)}%")
|
|
||||||
imgui.pop_style_color()
|
|
||||||
imgui.table_next_column()
|
|
||||||
if imgui.button(f"Load##{track.get('id')}"):
|
|
||||||
self._cb_load_track(str(track.get("id") or ""))
|
|
||||||
imgui.end_table()
|
|
||||||
# 1b. New Track Form
|
|
||||||
imgui.text("Create New Track")
|
|
||||||
changed_n, self.ui_new_track_name = imgui.input_text("Name##new_track", self.ui_new_track_name)
|
|
||||||
changed_d, self.ui_new_track_desc = imgui.input_text_multiline("Description##new_track", self.ui_new_track_desc, imgui.ImVec2(-1, 60))
|
|
||||||
imgui.text("Type:")
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.begin_combo("##track_type", self.ui_new_track_type):
|
|
||||||
for ttype in ["feature", "chore", "fix"]:
|
|
||||||
if imgui.selectable(ttype, self.ui_new_track_type == ttype)[0]:
|
|
||||||
self.ui_new_track_type = ttype
|
|
||||||
imgui.end_combo()
|
|
||||||
if imgui.button("Create Track"):
|
|
||||||
self._cb_create_track(self.ui_new_track_name, self.ui_new_track_desc, self.ui_new_track_type)
|
|
||||||
self.ui_new_track_name = ""
|
|
||||||
self.ui_new_track_desc = ""
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
# 2. Global Controls
|
self._render_mma_global_controls()
|
||||||
changed, self.mma_step_mode = imgui.checkbox("Step Mode (HITL)", self.mma_step_mode)
|
|
||||||
if changed:
|
|
||||||
# We could push an event here if the engine needs to know immediately
|
|
||||||
pass
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text(f"Status: {self.mma_status.upper()}")
|
|
||||||
if self.controller and hasattr(self.controller, 'engine') and self.controller.engine and hasattr(self.controller.engine, '_pause_event'):
|
|
||||||
imgui.same_line()
|
|
||||||
is_paused = self.controller.engine._pause_event.is_set()
|
|
||||||
label = "Resume" if is_paused else "Pause"
|
|
||||||
if imgui.button(label):
|
|
||||||
if is_paused:
|
|
||||||
self.controller.engine.resume()
|
|
||||||
else:
|
|
||||||
self.controller.engine.pause()
|
|
||||||
if self.active_tier:
|
|
||||||
imgui.same_line()
|
|
||||||
imgui.text_colored(C_VAL, f"| Active: {self.active_tier}")
|
|
||||||
# Approval pending indicator
|
|
||||||
any_pending = (
|
|
||||||
len(self._pending_mma_spawns) > 0 or
|
|
||||||
len(self._pending_mma_approvals) > 0 or
|
|
||||||
self._pending_ask_dialog
|
|
||||||
)
|
|
||||||
if any_pending:
|
|
||||||
alpha = abs(math.sin(time.time() * 5))
|
|
||||||
imgui.same_line()
|
|
||||||
c = imgui.ImVec4(1.0, 0.3, 0.3, alpha)
|
|
||||||
if is_nerv: c = vec4(255, 72, 64, alpha) # ALERT_RED
|
|
||||||
imgui.text_colored(c, " APPROVAL PENDING")
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.button("Go to Approval"):
|
|
||||||
pass # scroll/focus handled by existing dialog rendering
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
# 3. Token Usage Table
|
self._render_mma_usage_section()
|
||||||
imgui.text("Tier Usage (Tokens & Cost)")
|
|
||||||
if imgui.begin_table("mma_usage", 5, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg):
|
|
||||||
imgui.table_setup_column("Tier")
|
|
||||||
imgui.table_setup_column("Model")
|
|
||||||
imgui.table_setup_column("Input")
|
|
||||||
imgui.table_setup_column("Output")
|
|
||||||
imgui.table_setup_column("Est. Cost")
|
|
||||||
imgui.table_headers_row()
|
|
||||||
usage = self.mma_tier_usage
|
|
||||||
total_cost = 0.0
|
|
||||||
for tier, stats in usage.items():
|
|
||||||
imgui.table_next_row()
|
|
||||||
imgui.table_next_column()
|
|
||||||
imgui.text(tier)
|
|
||||||
imgui.table_next_column()
|
|
||||||
model = stats.get('model', 'unknown')
|
|
||||||
imgui.text(model)
|
|
||||||
imgui.table_next_column()
|
|
||||||
in_t = stats.get('input', 0)
|
|
||||||
imgui.text(f"{in_t:,}")
|
|
||||||
imgui.table_next_column()
|
|
||||||
out_t = stats.get('output', 0)
|
|
||||||
imgui.text(f"{out_t:,}")
|
|
||||||
imgui.table_next_column()
|
|
||||||
cost = cost_tracker.estimate_cost(model, in_t, out_t)
|
|
||||||
total_cost += cost
|
|
||||||
imgui.text(f"${cost:,.4f}")
|
|
||||||
# Total Row
|
|
||||||
imgui.table_next_row()
|
|
||||||
imgui.table_set_bg_color(imgui.TableBgTarget_.row_bg0, imgui.get_color_u32(imgui.Col_.plot_lines_hovered))
|
|
||||||
imgui.table_next_column()
|
|
||||||
imgui.text("TOTAL")
|
|
||||||
imgui.table_next_column()
|
|
||||||
imgui.text("")
|
|
||||||
imgui.table_next_column()
|
|
||||||
imgui.text("")
|
|
||||||
imgui.table_next_column()
|
|
||||||
imgui.text("")
|
|
||||||
imgui.table_next_column()
|
|
||||||
imgui.text(f"${total_cost:,.4f}")
|
|
||||||
imgui.end_table()
|
|
||||||
imgui.separator()
|
|
||||||
# 3b. Tier Model Config
|
|
||||||
if imgui.collapsing_header("Tier Model Config"):
|
|
||||||
for tier in self.mma_tier_usage.keys():
|
|
||||||
imgui.text(f"{tier}:")
|
|
||||||
imgui.same_line()
|
|
||||||
current_model = self.mma_tier_usage[tier].get("model", "unknown")
|
|
||||||
current_provider = self.mma_tier_usage[tier].get("provider", "gemini")
|
|
||||||
|
|
||||||
imgui.push_id(f"tier_cfg_{tier}")
|
|
||||||
|
|
||||||
# Provider selection
|
|
||||||
imgui.push_item_width(80)
|
|
||||||
if imgui.begin_combo("##prov", current_provider):
|
|
||||||
for p in models.PROVIDERS:
|
|
||||||
if imgui.selectable(p, p == current_provider)[0]:
|
|
||||||
self.mma_tier_usage[tier]["provider"] = p
|
|
||||||
# Reset model to default for provider
|
|
||||||
models_list = self.controller.all_available_models.get(p, [])
|
|
||||||
if models_list:
|
|
||||||
self.mma_tier_usage[tier]["model"] = models_list[0]
|
|
||||||
imgui.end_combo()
|
|
||||||
imgui.pop_item_width()
|
|
||||||
|
|
||||||
imgui.same_line()
|
|
||||||
|
|
||||||
# Model selection
|
|
||||||
imgui.push_item_width(150)
|
|
||||||
models_list = self.controller.all_available_models.get(current_provider, [])
|
|
||||||
if imgui.begin_combo("##model", current_model):
|
|
||||||
for model in models_list:
|
|
||||||
if imgui.selectable(model, current_model == model)[0]:
|
|
||||||
self.mma_tier_usage[tier]["model"] = model
|
|
||||||
imgui.end_combo()
|
|
||||||
imgui.pop_item_width()
|
|
||||||
|
|
||||||
imgui.same_line()
|
|
||||||
|
|
||||||
# Tool Preset selection
|
|
||||||
imgui.push_item_width(-1)
|
|
||||||
current_preset = self.mma_tier_usage[tier].get("tool_preset") or "None"
|
|
||||||
preset_names = ["None"] + sorted(self.controller.tool_presets.keys())
|
|
||||||
if imgui.begin_combo("##preset", current_preset):
|
|
||||||
for preset_name in preset_names:
|
|
||||||
if imgui.selectable(preset_name, current_preset == preset_name)[0]:
|
|
||||||
self.mma_tier_usage[tier]["tool_preset"] = None if preset_name == "None" else preset_name
|
|
||||||
imgui.end_combo()
|
|
||||||
imgui.pop_item_width()
|
|
||||||
|
|
||||||
imgui.same_line()
|
|
||||||
|
|
||||||
# Persona selection
|
|
||||||
imgui.push_item_width(150)
|
|
||||||
current_persona = self.mma_tier_usage[tier].get("persona") or "None"
|
|
||||||
personas = getattr(self.controller, 'personas', {})
|
|
||||||
persona_options = ["None"] + sorted(personas.keys())
|
|
||||||
if imgui.begin_combo("##persona", current_persona):
|
|
||||||
for persona_name in persona_options:
|
|
||||||
if imgui.selectable(persona_name, current_persona == persona_name)[0]:
|
|
||||||
self.mma_tier_usage[tier]["persona"] = None if persona_name == "None" else persona_name
|
|
||||||
imgui.end_combo()
|
|
||||||
imgui.pop_item_width()
|
|
||||||
|
|
||||||
imgui.pop_id()
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
self._render_ticket_queue()
|
self._render_ticket_queue()
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
ch, self.ui_separate_task_dag = imgui.checkbox("Pop Out Task DAG", self.ui_separate_task_dag)
|
self._render_window_if_open("Task DAG", self._render_task_dag_panel, not self.ui_separate_task_dag)
|
||||||
if ch:
|
if self.ui_selected_ticket_id: self._render_mma_ticket_editor()
|
||||||
self.show_windows["Task DAG"] = self.ui_separate_task_dag
|
|
||||||
|
|
||||||
if not self.ui_separate_task_dag:
|
|
||||||
self._render_task_dag_panel()
|
|
||||||
|
|
||||||
# 6. Edit Selected Ticket
|
|
||||||
if self.ui_selected_ticket_id:
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
imgui.text_colored(C_VAL, f"Editing: {self.ui_selected_ticket_id}")
|
self._render_mma_agent_streams()
|
||||||
ticket = next((t for t in self.active_tickets if str(t.get('id', '')) == self.ui_selected_ticket_id), None)
|
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_mma_dashboard")
|
||||||
if ticket:
|
|
||||||
imgui.text(f"Status: {ticket.get('status', 'todo')}")
|
|
||||||
prio = ticket.get('priority', 'medium')
|
|
||||||
imgui.text("Priority:")
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.begin_combo(f"##edit_prio_{ticket.get('id')}", prio):
|
|
||||||
for p_opt in ['high', 'medium', 'low']:
|
|
||||||
if imgui.selectable(p_opt, p_opt == prio)[0]:
|
|
||||||
ticket['priority'] = p_opt
|
|
||||||
self._push_mma_state_update()
|
|
||||||
imgui.end_combo()
|
|
||||||
imgui.text(f"Target: {ticket.get('target_file', '')}")
|
|
||||||
deps = ticket.get('depends_on', [])
|
|
||||||
imgui.text(f"Depends on: {', '.join(deps)}")
|
|
||||||
personas = getattr(self.controller, 'personas', {})
|
|
||||||
current_persona = ticket.get('persona_id', '')
|
|
||||||
imgui.text("Persona Override:")
|
|
||||||
imgui.same_line()
|
|
||||||
persona_options = ["None"] + sorted(personas.keys())
|
|
||||||
current_idx = persona_options.index(current_persona) + 1 if current_persona in persona_options else 0
|
|
||||||
_, current_idx = imgui.combo(f"##ticket_persona_{ticket.get('id')}", current_idx, persona_options)
|
|
||||||
if current_idx > 0:
|
|
||||||
ticket['persona_id'] = None if persona_options[current_idx] == "None" else persona_options[current_idx]
|
|
||||||
else:
|
|
||||||
ticket['persona_id'] = ""
|
|
||||||
if imgui.button(f"Mark Complete##{self.ui_selected_ticket_id}"):
|
|
||||||
ticket['status'] = 'done'
|
|
||||||
self._push_mma_state_update()
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.button(f"Delete##{self.ui_selected_ticket_id}"):
|
|
||||||
self.active_tickets = [t for t in self.active_tickets if str(t.get('id', '')) != self.ui_selected_ticket_id]
|
|
||||||
self.ui_selected_ticket_id = None
|
|
||||||
self._push_mma_state_update()
|
|
||||||
|
|
||||||
imgui.separator()
|
|
||||||
imgui.text("Agent Streams")
|
|
||||||
if imgui.begin_tab_bar("mma_streams_tabs"):
|
|
||||||
# Tier 1
|
|
||||||
if imgui.begin_tab_item("Tier 1")[0]:
|
|
||||||
ch, self.ui_separate_tier1 = imgui.checkbox("Pop Out Tier 1", self.ui_separate_tier1)
|
|
||||||
if ch: self.show_windows["Tier 1: Strategy"] = self.ui_separate_tier1
|
|
||||||
if not self.ui_separate_tier1:
|
|
||||||
self._render_tier_stream_panel("Tier 1", "Tier 1")
|
|
||||||
else:
|
|
||||||
imgui.text_disabled("Tier 1 stream is detached.")
|
|
||||||
imgui.end_tab_item()
|
|
||||||
# Tier 2
|
|
||||||
if imgui.begin_tab_item("Tier 2")[0]:
|
|
||||||
ch, self.ui_separate_tier2 = imgui.checkbox("Pop Out Tier 2", self.ui_separate_tier2)
|
|
||||||
if ch: self.show_windows["Tier 2: Tech Lead"] = self.ui_separate_tier2
|
|
||||||
if not self.ui_separate_tier2:
|
|
||||||
self._render_tier_stream_panel("Tier 2", "Tier 2 (Tech Lead)")
|
|
||||||
else:
|
|
||||||
imgui.text_disabled("Tier 2 stream is detached.")
|
|
||||||
imgui.end_tab_item()
|
|
||||||
# Tier 3
|
|
||||||
if imgui.begin_tab_item("Tier 3")[0]:
|
|
||||||
ch, self.ui_separate_tier3 = imgui.checkbox("Pop Out Tier 3", self.ui_separate_tier3)
|
|
||||||
if ch: self.show_windows["Tier 3: Workers"] = self.ui_separate_tier3
|
|
||||||
if not self.ui_separate_tier3:
|
|
||||||
self._render_tier_stream_panel("Tier 3", None)
|
|
||||||
else:
|
|
||||||
imgui.text_disabled("Tier 3 stream is detached.")
|
|
||||||
imgui.end_tab_item()
|
|
||||||
# Tier 4
|
|
||||||
if imgui.begin_tab_item("Tier 4")[0]:
|
|
||||||
ch, self.ui_separate_tier4 = imgui.checkbox("Pop Out Tier 4", self.ui_separate_tier4)
|
|
||||||
if ch: self.show_windows["Tier 4: QA"] = self.ui_separate_tier4
|
|
||||||
if not self.ui_separate_tier4:
|
|
||||||
self._render_tier_stream_panel("Tier 4", "Tier 4 (QA)")
|
|
||||||
else:
|
|
||||||
imgui.text_disabled("Tier 4 stream is detached.")
|
|
||||||
imgui.end_tab_item()
|
|
||||||
if getattr(self, "ui_project_execution_mode", "native") == "beads":
|
|
||||||
if imgui.begin_tab_item("Beads")[0]:
|
|
||||||
self._render_beads_tab()
|
|
||||||
imgui.end_tab_item()
|
|
||||||
imgui.end_tab_bar()
|
|
||||||
|
|
||||||
def _render_task_dag_panel(self) -> None:
|
def _render_task_dag_panel(self) -> None:
|
||||||
# 4. Task DAG Visualizer
|
# 4. Task DAG Visualizer
|
||||||
|
|||||||
Reference in New Issue
Block a user