Private
Public Access
0
0

feat(theme): lift all hardcoded colors and finalize semantic theming

This commit is contained in:
2026-06-05 00:21:19 -04:00
parent 7ea52cbbe8
commit 7735b6cba7
+70 -74
View File
@@ -76,10 +76,6 @@ def hide_tk_root() -> Tk:
root.wm_attributes("-topmost", True)
return root
# Standard Color Constants (normalized to 0-1)
def vec4(r: float, g: float, b: float, a: float = 1.0) -> imgui.ImVec4:
return imgui.ImVec4(r/255.0, g/255.0, b/255.0, a)
# Standard Color Constants (now bound to the theming system)
def C_OUT() -> imgui.ImVec4: return theme.get_color("status_info")
def C_IN() -> imgui.ImVec4: return theme.get_color("status_success")
@@ -885,7 +881,7 @@ class App:
win32gui.ReleaseCapture()
win32gui.SendMessage(hwnd, win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0)
imgui.push_style_color(imgui.Col_.button, vec4(0, 0, 0, 0))
imgui.push_style_color(imgui.Col_.button, imgui.ImVec4(0, 0, 0, 0))
try:
is_max = win32gui.GetWindowPlacement(hwnd)[1] == win32con.SW_SHOWMAXIMIZED
@@ -902,7 +898,7 @@ class App:
win32gui.ShowWindow(hwnd, win32con.SW_RESTORE if is_max else win32con.SW_MAXIMIZE)
imgui.set_cursor_pos((right_x + btn_w * 2, 0))
imgui.push_style_color(imgui.Col_.button_hovered, vec4(200, 50, 50, 255))
imgui.push_style_color(imgui.Col_.button_hovered, theme.get_color("status_error"))
if imgui.button("X", (btn_w, bar_h)):
win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
imgui.pop_style_color()
@@ -1499,7 +1495,7 @@ def render_cache_panel(app: App) -> None:
if app.current_provider != "gemini":
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_cache_panel")
return
imgui.text_colored(C_LBL, 'Cache Analytics')
imgui.text_colored(C_LBL(), 'Cache Analytics')
stats = getattr(app.controller, '_cached_cache_stats', {})
if not stats.get("cache_exists"):
imgui.text_disabled("No active cache")
@@ -1528,7 +1524,7 @@ def render_cache_panel(app: App) -> None:
def render_tool_analytics_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_tool_analytics_panel")
imgui.text_colored(C_LBL, 'Tool Usage')
imgui.text_colored(C_LBL(), 'Tool Usage')
imgui.separator()
now = time.time()
if not hasattr(app, '_tool_stats_cache_time') or now - app._tool_stats_cache_time > 1.0:
@@ -1566,14 +1562,14 @@ def render_tool_analytics_panel(app: App) -> None:
def render_token_budget_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_token_budget_panel")
imgui.text_colored(C_LBL, 'Prompt Utilization')
imgui.text_colored(C_LBL(), 'Prompt Utilization')
usage = app.session_usage
total = usage["input_tokens"] + usage["output_tokens"]
if total == 0 and usage.get("total_tokens", 0) > 0: total = usage["total_tokens"]
render_selectable_label(app, "session_telemetry_tokens", f"Tokens: {total:,} (In: {usage['input_tokens']:,} Out: {usage['output_tokens']:,})", width=-1, color=C_RES)
if usage.get("last_latency", 0.0) > 0: imgui.text_colored(C_LBL, f" Last Latency: {usage['last_latency']:.2f}s")
if usage["cache_read_input_tokens"]: imgui.text_colored(C_LBL, f" Cache Read: {usage['cache_read_input_tokens']:,} Creation: {usage['cache_creation_input_tokens']:,}")
if app._gemini_cache_text: imgui.text_colored(C_SUB, app._gemini_cache_text)
render_selectable_label(app, "session_telemetry_tokens", f"Tokens: {total:,} (In: {usage['input_tokens']:,} Out: {usage['output_tokens']:,})", width=-1, color=C_RES())
if usage.get("last_latency", 0.0) > 0: imgui.text_colored(C_LBL(), f" Last Latency: {usage['last_latency']:.2f}s")
if usage["cache_read_input_tokens"]: imgui.text_colored(C_LBL(), f" Cache Read: {usage['cache_read_input_tokens']:,} Creation: {usage['cache_creation_input_tokens']:,}")
if app._gemini_cache_text: imgui.text_colored(C_SUB(), app._gemini_cache_text)
imgui.separator()
if app._token_stats_dirty:
@@ -1655,7 +1651,7 @@ def render_token_budget_panel(app: App) -> None:
if cache_stats.get("cache_exists"):
age = cache_stats.get("cache_age_seconds", 0)
ttl = cache_stats.get("ttl_seconds", 3600)
imgui.text_colored(C_LBL, f"Cache Usage: ACTIVE | Age: {age:.0f}s / {ttl}s | Renews at: {ttl * 0.9:.0f}s")
imgui.text_colored(C_LBL(), f"Cache Usage: ACTIVE | Age: {age:.0f}s / {ttl}s | Renews at: {ttl * 0.9:.0f}s")
else:
imgui.text_disabled("Cache Usage: INACTIVE")
if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_token_budget_panel")
@@ -1757,7 +1753,7 @@ def render_project_settings_hub(app: App) -> None:
def render_projects_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_projects_panel")
proj_name = app.project.get("project", {}).get("name", Path(app.active_project_path).stem)
imgui.text_colored(C_IN, f"Active: {proj_name}")
imgui.text_colored(C_IN(), f"Active: {proj_name}")
imgui.separator()
imgui.text("Execution Mode")
modes = ["native", "beads"]
@@ -1802,11 +1798,11 @@ def render_projects_panel(app: App) -> None:
break
imgui.same_line()
marker = " *" if is_active else ""
if is_active: imgui.push_style_color(imgui.Col_.text, C_IN)
if is_active: imgui.push_style_color(imgui.Col_.text, C_IN())
if imgui.button(f"{Path(pp).stem}{marker}##ps{i}"): app._switch_project(pp)
if is_active: imgui.pop_style_color()
imgui.same_line()
imgui.text_colored(C_LBL, pp)
imgui.text_colored(C_LBL(), pp)
imgui.end_child()
if imgui.button("Add Project"):
r = hide_tk_root()
@@ -1843,7 +1839,7 @@ def render_paths_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_paths_panel")
path_info = paths.get_full_path_info()
imgui.text_colored(C_IN, "System Path Configuration")
imgui.text_colored(C_IN(), "System Path Configuration")
imgui.separator()
def render_path_field(label: str, attr: str, key: str, tooltip: str):
@@ -2186,7 +2182,7 @@ def render_base_prompt_diff_modal(app: App) -> None:
return
imgui.open_popup("Base Prompt Diff")
if imgui.begin_popup_modal("Base Prompt Diff", True, imgui.WindowFlags_.always_auto_resize)[0]:
imgui.text_colored(C_IN, "Difference between Default and Custom Base System Prompt")
imgui.text_colored(C_IN(), "Difference between Default and Custom Base System Prompt")
imgui.separator()
default_lines = ai_client._SYSTEM_PROMPT.splitlines(keepends=True)
@@ -2269,7 +2265,7 @@ def render_preset_manager_content(app: App, is_embedded: bool = False) -> None:
imgui.begin_child("prompt_edit_pane", imgui.ImVec2(0, avail_r.y - 45), False)
if True:
p_disp = app._editing_preset_name or "(New Preset)"
imgui.text_colored(C_IN, f"Editing Prompt Preset: {p_disp}")
imgui.text_colored(C_IN(), f"Editing Prompt Preset: {p_disp}")
imgui.separator()
if imgui.begin_table("p_meta", 2):
@@ -2369,7 +2365,7 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N
imgui.begin_child("tp_editor_content", imgui.ImVec2(0, avail_r.y - 45), False)
if True:
p_name = app._editing_tool_preset_name or "(New Tool Preset)"
imgui.text_colored(C_IN, f"Editing Tool Preset: {p_name}"); imgui.separator()
imgui.text_colored(C_IN(), f"Editing Tool Preset: {p_name}"); imgui.separator()
if imgui.begin_table("tp_meta", 2):
imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 80); imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch)
@@ -2564,7 +2560,7 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None:
imgui.begin_child("persona_editor_content", imgui.ImVec2(0, avail.y - 45), False)
if True:
header_text = "New Persona" if getattr(app, '_editing_persona_is_new', True) else f"Editing Persona: {app._editing_persona_name}"
imgui.text_colored(C_IN, header_text); imgui.separator()
imgui.text_colored(C_IN(), header_text); imgui.separator()
if imgui.begin_table("p_meta", 2):
imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 60); imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch)
@@ -2595,10 +2591,10 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None:
imgui.push_id(f"pref_model_{i}")
prov, mod, is_expanded = entry.get("provider", "Unknown"), entry.get("model", "Unknown"), app._persona_pref_models_expanded.get(i, False)
if imgui.button("-" if is_expanded else "+"): app._persona_pref_models_expanded[i] = not is_expanded
imgui.same_line(); imgui.text(f"{i+1}."); imgui.same_line(); imgui.text_colored(C_LBL, f"{prov}"); imgui.same_line(); imgui.text("-"); imgui.same_line(); imgui.text_colored(C_IN, f"{mod}")
imgui.same_line(); imgui.text(f"{i+1}."); imgui.same_line(); imgui.text_colored(C_LBL(), f"{prov}"); imgui.same_line(); imgui.text("-"); imgui.same_line(); imgui.text_colored(C_IN(), f"{mod}")
if not is_expanded:
imgui.same_line(); summary = f" (T:{entry.get('temperature', 0.7):.1f}, P:{entry.get('top_p', 1.0):.2f}, M:{entry.get('max_output_tokens', 0)})"
imgui.text_colored(C_SUB, summary)
imgui.text_colored(C_SUB(), summary)
imgui.same_line(imgui.get_content_region_avail().x - 30);
if imgui.button("x"): to_remove.append(i)
if is_expanded:
@@ -2994,7 +2990,7 @@ def render_ast_inspector_modal(app: App) -> None:
imgui.separator()
if imgui.collapsing_header("Custom Slices", imgui.TreeNodeFlags_.default_open):
if not hasattr(f_item, 'custom_slices'): f_item.custom_slices = []
imgui.text_colored(C_IN, "Highlight lines in right pane to add slices.")
imgui.text_colored(C_IN(), "Highlight lines in right pane to add slices.")
if imgui.button("Add Selection as Slice"):
if getattr(app, '_slice_sel_start', -1) != -1 and getattr(app, '_slice_sel_end', -1) != -1:
s_line = min(app._slice_sel_start, app._slice_sel_end)
@@ -3112,7 +3108,7 @@ def render_save_workspace_profile_modal(app: App) -> None:
imgui.end_popup()
def render_context_presets_panel(app: App) -> None:
imgui.text_colored(C_IN, "Context Presets")
imgui.text_colored(C_IN(), "Context Presets")
imgui.separator()
changed, new_name = imgui.input_text("Preset Name##new_ctx", app.ui_new_context_preset_name)
if changed: app.ui_new_context_preset_name = new_name
@@ -3402,14 +3398,14 @@ def render_thinking_trace(app: App, entry: dict, segments: list[dict], entry_ind
if imgui.button(f"[Pure]##think_pure_{entry_index}" if thinking_read_mode else f"[Read]##think_read_{entry_index}"):
entry["thinking_read_mode"] = not thinking_read_mode
imgui.same_line()
imgui.text_colored(C_TC, "Selectable toggle")
imgui.text_colored(C_TC(), "Selectable toggle")
h = 150 if is_standalone else 100
with imscope.child(f"thinking_content_{entry_index}", 0, h, True):
for idx, seg in enumerate(segments):
content = seg.get("content", "")
marker = seg.get("marker", "thinking")
with imscope.id(f"think_{entry_index}_{idx}"):
imgui.text_colored(C_TC, f"[{marker}]")
imgui.text_colored(C_TC(), f"[{marker}]")
if thinking_read_mode:
if app.ui_word_wrap:
with imscope.text_wrap(imgui.get_content_region_avail().x):
@@ -3455,7 +3451,7 @@ def render_discussion_entry(app: App, entry: dict, index: int) -> None:
usage = entry.get("usage", {})
if ts_str or usage:
imgui.same_line()
if ts_str: imgui.text_colored(C_SUB, str(ts_str))
if ts_str: imgui.text_colored(C_SUB(), str(ts_str))
if usage:
inp, out, cache = usage.get("input_tokens", 0), usage.get("output_tokens", 0), usage.get("cache_read_input_tokens", 0)
u_str = f" in:{inp} out:{out}" + (f" cache:{cache}" if cache else "")
@@ -3474,7 +3470,7 @@ def render_discussion_entry(app: App, entry: dict, index: int) -> None:
if imgui.button("Branch"): app._branch_discussion(index)
imgui.same_line(); preview = entry["content"].replace("\n", " ")[:60]
if len(entry["content"]) > 60: preview += "..."
imgui.text_colored(C_SUB, preview)
imgui.text_colored(C_SUB(), preview)
else:
# Body content - FORCE START ON NEW LINE to prevent horizontal squashing
imgui.new_line()
@@ -3575,7 +3571,7 @@ def render_history_window(app: App) -> None:
def render_session_insights_panel(app: App) -> None:
if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_session_insights_panel")
imgui.text_colored(C_LBL, 'Session Insights')
imgui.text_colored(C_LBL(), 'Session Insights')
imgui.separator()
insights = app.controller.get_session_insights()
imgui.text(f"Total Tokens: {insights.get('total_tokens', 0):,}")
@@ -3597,7 +3593,7 @@ def render_prior_session_view(app: App) -> None:
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}]")
imgui.text_colored(C_LBL(), f"[{role}]")
if ts: imgui.same_line(); imgui.text_colored(theme.get_color("text_disabled"), str(ts))
content = entry.get("content", "")
if collapsed:
@@ -3613,7 +3609,7 @@ def render_thinking_indicator(app: App) -> None:
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)
c = theme.get_color("status_error", alpha=alpha)
imgui.text_colored(c, "THINKING..."); imgui.same_line()
def render_synthesis_panel(app: App) -> None:
@@ -3665,13 +3661,13 @@ def render_comms_history_panel(app: App) -> None:
app._comms_log_dirty = True
imgui.separator()
imgui.text_colored(C_OUT, "OUT"); imgui.same_line()
imgui.text_colored(C_REQ, "request"); imgui.same_line()
imgui.text_colored(C_TC, "tool_call"); imgui.same_line()
imgui.text_colored(C_OUT(), "OUT"); imgui.same_line()
imgui.text_colored(C_REQ(), "request"); imgui.same_line()
imgui.text_colored(C_TC(), "tool_call"); imgui.same_line()
imgui.text(" "); imgui.same_line()
imgui.text_colored(C_IN, "IN"); imgui.same_line()
imgui.text_colored(C_RES, "response"); imgui.same_line()
imgui.text_colored(C_TR, "tool_result")
imgui.text_colored(C_IN(), "IN"); imgui.same_line()
imgui.text_colored(C_RES(), "response"); imgui.same_line()
imgui.text_colored(C_TR(), "tool_result")
imgui.separator()
avail = imgui.get_content_region_avail()
@@ -3693,25 +3689,25 @@ def render_comms_history_panel(app: App) -> None:
payload = entry # legacy
# Row 1: #Idx TS DIR KIND Provider/Model [Tier]
imgui.text_colored(C_LBL, f"#{i_display}"); imgui.same_line()
imgui.text_colored(C_LBL(), f"#{i_display}"); imgui.same_line()
imgui.text_colored(theme.get_color("text_disabled"), ts)
latency = entry.get("latency") or entry.get("metadata", {}).get("latency")
if latency:
imgui.same_line()
imgui.text_colored(C_SUB, f" ({latency:.2f}s)")
imgui.text_colored(C_SUB(), f" ({latency:.2f}s)")
ticket_id = entry.get("mma_ticket_id")
if ticket_id:
imgui.same_line()
imgui.text_colored(theme.get_color("status_error"), f"[{ticket_id}]")
imgui.same_line()
d_col = DIR_COLORS.get(direction, C_VAL)
d_col = DIR_COLORS.get(direction, C_VAL())
imgui.text_colored(d_col, direction); imgui.same_line()
k_col = KIND_COLORS.get(kind, C_VAL)
k_col = KIND_COLORS.get(kind, C_VAL())
imgui.text_colored(k_col, kind); imgui.same_line()
imgui.text_colored(C_LBL, f"{provider}/{model}"); imgui.same_line()
imgui.text_colored(C_SUB, f"[{tier}]")
imgui.text_colored(C_LBL(), f"{provider}/{model}"); imgui.same_line()
imgui.text_colored(C_SUB(), f"[{tier}]")
# Optimized content rendering using _render_heavy_text logic
idx_str = str(i)
@@ -3719,7 +3715,7 @@ def render_comms_history_panel(app: App) -> None:
usage = payload.get("usage", {})
if usage:
inp = usage.get("input_tokens", 0)
imgui.text_colored(C_LBL, f" tokens in:{inp}")
imgui.text_colored(C_LBL(), f" tokens in:{inp}")
render_heavy_text(app, "message", payload.get("message", ""), idx_str)
if payload.get("system"):
render_heavy_text(app, "system", payload.get("system", ""), idx_str)
@@ -3734,7 +3730,7 @@ def render_comms_history_panel(app: App) -> None:
cache = usage.get("cache_read_input_tokens", 0)
usage_str = f" in:{inp} out:{out}"
if cache: usage_str += f" cache:{cache}"
imgui.text_colored(C_LBL, f"round: {r} stop_reason: {sr}{usage_str}")
imgui.text_colored(C_LBL(), f"round: {r} stop_reason: {sr}{usage_str}")
text_content = payload.get("text", "")
segments, parsed_response = thinking_parser.parse_thinking_trace(text_content)
@@ -3774,7 +3770,7 @@ def render_takes_panel(app: App) -> None:
imgui.table_set_column_index(0)
is_active = name == app.active_discussion
if is_active:
imgui.text_colored(C_IN, name)
imgui.text_colored(C_IN(), name)
else:
imgui.text(name)
imgui.table_set_column_index(1)
@@ -3861,14 +3857,14 @@ def render_discussion_metadata(app: App) -> None:
imgui.text_colored(theme.get_color("status_info"), f"Discussion Tokens: {total_in} In | {total_out} Out | {total_cache} Cache")
imgui.separator()
imgui.text_colored(C_LBL, "commit:"); imgui.same_line()
render_selectable_label(app, 'git_commit_val', git_commit[:12] if git_commit else '(none)', width=100, color=(C_IN if git_commit else C_LBL))
imgui.text_colored(C_LBL(), "commit:"); imgui.same_line()
render_selectable_label(app, '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 app.ui_project_git_dir:
cmt = project_manager.get_git_commit(app.ui_project_git_dir)
if cmt: disc_data["git_commit"], disc_data["last_updated"], app.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)")
imgui.text_colored(C_LBL(), "updated:"); imgui.same_line(); imgui.text_colored(C_SUB(), last_updated if last_updated else "(never)")
ch, app.ui_disc_new_name_input = imgui.input_text("##new_disc", app.ui_disc_new_name_input); imgui.same_line()
if imgui.button("Create"):
nm = app.ui_disc_new_name_input.strip()
@@ -4028,7 +4024,7 @@ def render_operations_hub(app: App) -> None:
render_external_tools_panel(app)
imgui.separator(); imgui.text("")
try: render_external_editor_panel(app)
except Exception as e: imgui.text_colored(vec4(1, 0.3, 0.3, 1), f"Error: {str(e)}")
except Exception as e: imgui.text_colored(theme.get_color("status_error"), f"Error: {str(e)}")
with imscope.tab_item("Workspace Layouts") as (exp, _):
if exp:
imgui.text("Experimental: Auto-switch layout by Tier")
@@ -4114,7 +4110,7 @@ def render_response_panel(app: App) -> None:
except:
pass
is_blinking = False
blink_color = vec4(0, 0, 0, 0)
blink_color = imgui.ImVec4(0, 0, 0, 0)
if app._is_blinking:
elapsed = time.time() - app._blink_start_time
if elapsed > 1.5:
@@ -4123,7 +4119,7 @@ def render_response_panel(app: App) -> None:
is_blinking = True
val = math.sin(elapsed * 8 * math.pi)
alpha = 50/255 if val > 0 else 0
blink_color = vec4(0, 255, 0, alpha)
blink_color = theme.get_color("status_success", alpha=alpha)
with imscope.style_color(imgui.Col_.frame_bg, blink_color) if is_blinking else nullcontext():
with imscope.style_color(imgui.Col_.child_bg, blink_color) if is_blinking else nullcontext():
@@ -4180,7 +4176,7 @@ def render_tool_calls_panel(app: App) -> None:
app.show_windows["Text Viewer"] = True
imgui.table_next_column()
imgui.text_colored(C_SUB, f"[{entry.get('source_tier', 'main')}]")
imgui.text_colored(C_SUB(), f"[{entry.get('source_tier', 'main')}]")
imgui.table_next_column()
script_preview = script.replace("\n", " ")[:150]
@@ -4256,7 +4252,7 @@ def render_text_viewer_window(app: App) -> None:
app._slice_sel_end = -1
if expanded:
if app.ui_editing_slices_file is not None:
imgui.text_colored(C_IN, "Slice Management (Click-drag lines to select range)")
imgui.text_colored(C_IN(), "Slice Management (Click-drag lines to select range)")
if imgui.button("Add Selection as Slice"):
if app._slice_sel_start != -1 and app._slice_sel_end != -1:
s_line = min(app._slice_sel_start, app._slice_sel_end)
@@ -4401,7 +4397,7 @@ def render_external_editor_panel(app: App) -> None:
editors = launcher.config.editors
default_name = launcher.config.default_editor
if not editors:
imgui.text_colored(C_REQ, " No editors configured")
imgui.text_colored(C_REQ(), " No editors configured")
imgui.text("")
imgui.text("Add editors in config.toml:")
imgui.text(" [tools.text_editors.vscode]")
@@ -4428,7 +4424,7 @@ def render_external_editor_panel(app: App) -> None:
if not editor: continue
is_default = name == default_name
marker = " (default)" if is_default else ""
if is_default: imgui.text_colored(C_IN, f" {name}{marker}")
if is_default: imgui.text_colored(C_IN(), f" {name}{marker}")
else: imgui.text(f" {name}{marker}")
imgui.text(f" {editor.path}")
if editor.diff_args: imgui.textDisabled(f" diff: {editor.diff_args}")
@@ -4436,7 +4432,7 @@ def render_external_editor_panel(app: App) -> None:
imgui.text("Config: config.toml [tools.text_editors]")
imgui.text("Override: manual_slop.toml default_editor")
except Exception as e:
imgui.text_colored(C_TC, f"Error: {str(e)}")
imgui.text_colored(C_TC(), f"Error: {str(e)}")
def render_approve_script_modal(app: App) -> None:
"""Renders the modal dialog for approving AI-generated PowerShell scripts."""
@@ -4670,8 +4666,8 @@ def render_heavy_text(app: App, label: str, content: str, id_suffix: str = "") -
app.text_viewer_content = content
app.show_windows["Text Viewer"] = True
imgui.same_line()
imgui.text_colored(C_LBL, f"{label}:"); imgui.same_line()
render_selectable_label(app, f"heavy_label_{label}_{id_suffix}", content[:60].replace("\n", " ") + ("..." if len(content)>60 else ""), color=C_VAL)
imgui.text_colored(C_LBL(), f"{label}:"); imgui.same_line()
render_selectable_label(app, f"heavy_label_{label}_{id_suffix}", content[:60].replace("\n", " ") + ("..." if len(content)>60 else ""), color=C_VAL())
if content:
ctx_id = f"{label}_{id_suffix}"
@@ -4810,7 +4806,7 @@ def render_mma_track_summary(app: App) -> None:
if getattr(app, "ui_project_execution_mode", "native") == "beads": track_name = "Beads Graph"
track_stats = project_manager.calculate_track_progress(app.active_track.tickets if app.active_track else app.active_tickets)
total_cost = sum(cost_tracker.estimate_cost(u.get('model','unknown'), u.get('input',0), u.get('output',0)) for u in app.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()
imgui.text("Track:"); imgui.same_line(); imgui.text_colored(C_VAL(), track_name); imgui.same_line(); imgui.text(" | Status:"); imgui.same_line()
if app.mma_status == "paused":
imgui.text_colored(theme.get_color("status_warning") if is_nerv else theme.get_color("status_warning"), "PIPELINE PAUSED"); imgui.same_line()
status_col = imgui.ImVec4(1, 1, 1, 1)
@@ -4825,18 +4821,18 @@ def render_mma_track_summary(app: App) -> None:
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.table_next_column(); imgui.text_colored(C_LBL(), lbl); imgui.same_line(); imgui.text_colored(C_VAL(), str(val))
imgui.end_table()
if app.active_track:
remaining = track_stats["total"] - track_stats["completed"]
eta_mins = (app._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.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(app: App) -> None:
"""
[C: tests/test_gui_progress.py:test_render_mma_dashboard_progress]
"""
imgui.text_colored(C_LBL, 'Epic Planning (Tier 1)')
imgui.text_colored(C_LBL(), 'Epic Planning (Tier 1)')
_, app.ui_epic_input = imgui.input_text_multiline('##epic_input', app.ui_epic_input, imgui.ImVec2(-1, 80))
if imgui.button('Plan Epic (Tier 1)', imgui.ImVec2(-1, 0)): app._cb_plan_epic()
@@ -4889,11 +4885,11 @@ def render_mma_global_controls(app: App) -> None:
if is_paused: app.controller.engine.resume()
else: app.controller.engine.pause()
if app.active_tier:
imgui.same_line(); imgui.text_colored(C_VAL, f"| Active: {app.active_tier}")
imgui.same_line(); imgui.text_colored(C_VAL(), f"| Active: {app.active_tier}")
any_pending = len(app._pending_mma_spawns) > 0 or len(app._pending_mma_approvals) > 0 or app._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)
c = theme.get_color("status_error", alpha=alpha)
imgui.same_line(); imgui.text_colored(c, " APPROVAL PENDING"); imgui.same_line()
if imgui.button("Go to Approval"): pass
imgui.separator()
@@ -4954,7 +4950,7 @@ def render_mma_usage_section(app: App) -> None:
imgui.pop_item_width()
def render_mma_ticket_editor(app: App) -> None:
imgui.separator(); imgui.text_colored(C_VAL, f"Editing: {app.ui_selected_ticket_id}")
imgui.separator(); imgui.text_colored(C_VAL(), f"Editing: {app.ui_selected_ticket_id}")
ticket = next((t for t in app.active_tickets if str(t.get('id', '')) == app.ui_selected_ticket_id), None)
if ticket:
imgui.text(f"Status: {ticket.get('status', 'todo')}"); prio = ticket.get('priority', 'medium')
@@ -5054,7 +5050,7 @@ def render_track_proposal_modal(app: App) -> None:
shaders.draw_soft_shadow(imgui.get_background_draw_list(), p_min, p_max, imgui.ImVec4(0, 0, 0, 0.6), 25.0, 6.0)
if app._show_track_proposal_modal:
imgui.text_colored(C_IN, "Proposed Implementation Tracks")
imgui.text_colored(C_IN(), "Proposed Implementation Tracks")
imgui.separator()
if not app.proposed_tracks:
imgui.text("No tracks generated.")
@@ -5225,11 +5221,11 @@ def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer
if getattr(app, "ui_project_execution_mode", "native") == "beads":
imgui.text_colored(theme.get_color("status_info"), "[B] ")
imgui.same_line()
imgui.text_colored(C_KEY, f"Ticket: {tid}")
imgui.text_colored(C_KEY(), f"Ticket: {tid}")
status = t.get('status', 'todo')
s_col = C_VAL
if status == 'done' or status == 'complete': s_col = C_IN
elif status == 'in_progress' or status == 'running': s_col = C_OUT
s_col = C_VAL()
if status == 'done' or status == 'complete': s_col = C_IN()
elif status == 'in_progress' or status == 'running': s_col = C_OUT()
elif status == 'error': s_col = theme.get_color("status_error")
imgui.text("Status: ")
imgui.same_line()
@@ -5315,7 +5311,7 @@ def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer
app.ui_new_ticket_deps = ""
if app._show_add_ticket_form:
imgui.begin_child("add_ticket_form", imgui.ImVec2(-1, 220), True)
imgui.text_colored(C_VAL, "New Ticket Details")
imgui.text_colored(C_VAL(), "New Ticket Details")
_, app.ui_new_ticket_id = imgui.input_text("ID##new_ticket", app.ui_new_ticket_id)
_, app.ui_new_ticket_desc = imgui.input_text_multiline("Description##new_ticket", app.ui_new_ticket_desc, imgui.ImVec2(-1, 60))
_, app.ui_new_ticket_target = imgui.input_text("Target File##new_ticket", app.ui_new_ticket_target)