feat(theme): lift all hardcoded colors and finalize semantic theming
This commit is contained in:
+70
-74
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user