From ff1a9d77f77e805bd13fb17c10c374b1ce3147b4 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Tue, 12 May 2026 19:50:46 -0400 Subject: [PATCH] gemini 3.1 fails --- src/gui_2.py | 827 +++++++++++++++++++++----------------------- src/imgui_scopes.py | 36 ++ 2 files changed, 425 insertions(+), 438 deletions(-) diff --git a/src/gui_2.py b/src/gui_2.py index 52d77bf..9a10026 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -15,7 +15,7 @@ import threading import time # from defer import defer import tomli_w -# from contextlib import ExitStack +# from contextlib import ExitStack, nullcontext import traceback import typing from pathlib import Path @@ -605,53 +605,53 @@ class App: def _render_thinking_trace(self, segments: list[dict], entry_index: int, is_standalone: bool = False) -> None: if not segments: return - imgui.push_style_color(imgui.Col_.child_bg, vec4(40, 35, 25, 180)) - imgui.push_style_color(imgui.Col_.text, vec4(200, 200, 150)) - imgui.indent() + with imscope.style_color(imgui.Col_.child_bg, vec4(40, 35, 25, 180)), \ + imscope.style_color(imgui.Col_.text, vec4(200, 200, 150)): + imgui.indent() - show_content = True - if not is_standalone: - header_label = f"Monologue ({len(segments)} traces)###thinking_header_{entry_index}" - show_content = imgui.collapsing_header(header_label) + show_content = True + if not is_standalone: + header_label = f"Monologue ({len(segments)} traces)###thinking_header_{entry_index}" + show_content = imgui.collapsing_header(header_label) - if show_content: - h = 150 if is_standalone else 100 - with imscope.child(f"thinking_content_{entry_index}", imgui.ImVec2(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(vec4(180, 150, 80), f"[{marker}]") - if self.ui_word_wrap: - imscope.text_wrap(imgui.get_content_region_avail().x) - imgui.text_colored(vec4(200, 200, 150), content) - else: - imgui.text_colored(vec4(200, 200, 150), content) - imgui.separator() + if show_content: + h = 150 if is_standalone else 100 + with imscope.child(f"thinking_content_{entry_index}", imgui.ImVec2(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(vec4(180, 150, 80), f"[{marker}]") + if self.ui_word_wrap: + with imscope.text_wrap(imgui.get_content_region_avail().x): + imgui.text_colored(vec4(200, 200, 150), content) + else: + imgui.text_colored(vec4(200, 200, 150), content) + imgui.separator() - imgui.unindent() - imgui.pop_style_color(2) + imgui.unindent() def _render_selectable_label(self, label: str, value: str, width: float = 0.0, multiline: bool = False, height: float = 0.0, color: Optional[imgui.ImVec4] = None) -> None: - imgui.push_id(label + str(hash(value))) - pops = 4 - imgui.push_style_color(imgui.Col_.frame_bg, vec4(0, 0, 0, 0)) - imgui.push_style_color(imgui.Col_.frame_bg_hovered, vec4(0, 0, 0, 0)) - imgui.push_style_color(imgui.Col_.frame_bg_active, vec4(0, 0, 0, 0)) - imgui.push_style_color(imgui.Col_.border, vec4(0, 0, 0, 0)) - if color: - imgui.push_style_color(imgui.Col_.text, color) - pops += 1 - imgui.push_style_var(imgui.StyleVar_.frame_border_size, 0.0) - imgui.push_style_var(imgui.StyleVar_.frame_padding, imgui.ImVec2(0, 0)) - if multiline: - imgui.input_text_multiline("##" + label, value, imgui.ImVec2(width, height), imgui.InputTextFlags_.read_only) - else: - if width > 0: imgui.set_next_item_width(width) - imgui.input_text("##" + label, value, imgui.InputTextFlags_.read_only) - imgui.pop_style_color(pops) - imgui.pop_style_var(2) - imgui.pop_id() + with imscope.id(label + str(hash(value))): + with imscope.style_color(imgui.Col_.frame_bg, vec4(0, 0, 0, 0)), \ + imscope.style_color(imgui.Col_.frame_bg_hovered, vec4(0, 0, 0, 0)), \ + imscope.style_color(imgui.Col_.frame_bg_active, vec4(0, 0, 0, 0)), \ + imscope.style_color(imgui.Col_.border, vec4(0, 0, 0, 0)): + with imscope.style_var(imgui.StyleVar_.frame_border_size, 0.0), \ + imscope.style_var(imgui.StyleVar_.frame_padding, imgui.ImVec2(0, 0)): + if color: + with imscope.style_color(imgui.Col_.text, color): + if multiline: + imgui.input_text_multiline("##" + label, value, imgui.ImVec2(width, height), imgui.InputTextFlags_.read_only) + else: + if width > 0: imgui.set_next_item_width(width) + imgui.input_text("##" + label, value, imgui.InputTextFlags_.read_only) + else: + if multiline: + imgui.input_text_multiline("##" + label, value, imgui.ImVec2(width, height), imgui.InputTextFlags_.read_only) + else: + if width > 0: imgui.set_next_item_width(width) + imgui.input_text("##" + label, value, imgui.InputTextFlags_.read_only) 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.""" @@ -1220,34 +1220,30 @@ class App: 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: + with imscope.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() + with imscope.style_color(imgui.Col_.text, vec4(80, 255, 80)) if is_nerv else nullcontext(): + markdown_helper.render(content, context_id=f'prior_disc_{idx}') + imgui.separator() if not imgui.collapsing_header("Discussions", imgui.TreeNodeFlags_.default_open): return names = self._get_discussion_names(); grouped = {} for name in names: @@ -2204,11 +2200,12 @@ class App: if not self.show_preset_manager_window and not is_embedded: return if not is_embedded: imgui.set_next_window_size(imgui.ImVec2(1000, 800), imgui.Cond_.first_use_ever) - opened, self.show_preset_manager_window = imgui.begin("Prompt Presets Manager", self.show_preset_manager_window) - if not opened: - imgui.end(); return - self._render_preset_manager_content(is_embedded=is_embedded) - if not is_embedded: imgui.end() + with imscope.window("Prompt Presets Manager", self.show_preset_manager_window) as (opened, visible): + self.show_preset_manager_window = visible + if opened: + self._render_preset_manager_content(is_embedded=is_embedded) + else: + self._render_preset_manager_content(is_embedded=is_embedded) def _render_tool_preset_manager_content(self, is_embedded: bool = False) -> None: avail = imgui.get_content_region_avail() @@ -2382,16 +2379,16 @@ class App: if not is_embedded: if imgui.button("Close##tp", imgui.ImVec2(100, 0)): self.show_tool_preset_manager_window = False imgui.end_table() - def _render_tool_preset_manager_window(self, is_embedded: bool = False) -> None: if not self.show_tool_preset_manager_window and not is_embedded: return if not is_embedded: imgui.set_next_window_size(imgui.ImVec2(1000, 800), imgui.Cond_.first_use_ever) - opened, self.show_tool_preset_manager_window = imgui.begin("Tool Preset Manager", self.show_tool_preset_manager_window) - if not opened: - imgui.end(); return - self._render_tool_preset_manager_content(is_embedded=is_embedded) - if not is_embedded: imgui.end() + with imscope.window("Tool Preset Manager", self.show_tool_preset_manager_window) as (opened, visible): + self.show_tool_preset_manager_window = visible + if opened: + self._render_tool_preset_manager_content(is_embedded=is_embedded) + else: + self._render_preset_manager_content(is_embedded=is_embedded) def _render_persona_editor_window(self, is_embedded: bool = False) -> None: if not self.show_persona_editor_window and not is_embedded: return @@ -2557,7 +2554,6 @@ class App: if imgui.button("Close##pers", imgui.ImVec2(100, 0)): self.show_persona_editor_window = False imgui.end_table() - if not is_embedded: imgui.end() def _render_projects_panel(self) -> None: if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_projects_panel") @@ -2908,192 +2904,183 @@ class App: [C: tests/test_log_management_ui.py:test_render_log_management_logic] """ if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_log_management") - exp, opened = imgui.begin("Log Management", self.show_windows["Log Management"]) - self.show_windows["Log Management"] = bool(opened) - if not exp: - imgui.end() - return - - if self._log_registry is None: - self._log_registry = log_registry.LogRegistry(str(paths.get_logs_dir() / "log_registry.toml")) - else: - if imgui.button("Refresh Registry"): - self._log_registry = log_registry.LogRegistry(str(paths.get_logs_dir() / "log_registry.toml")) - imgui.same_line() - if imgui.button("Load Log"): - self.cb_load_prior_log() - imgui.same_line() - if imgui.button("Force Prune Logs"): - self.controller.event_queue.put("gui_task", {"action": "click", "item": "btn_prune_logs"}) - - registry = self._log_registry - sessions = registry.data - if imgui.begin_table("sessions_table", 7, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable): - imgui.table_setup_column("Session ID") - imgui.table_setup_column("Start Time") - imgui.table_setup_column("Star") - imgui.table_setup_column("Reason") - imgui.table_setup_column("Size (KB)") - imgui.table_setup_column("Msgs") - imgui.table_setup_column("Actions") - imgui.table_headers_row() - for session_id, s_data in sessions.items(): - imgui.table_next_row() - imgui.table_next_column() - imgui.text(session_id) - imgui.table_next_column() - imgui.text(s_data.get("start_time", "")) - imgui.table_next_column() - whitelisted = s_data.get("whitelisted", False) - if whitelisted: - imgui.text_colored(vec4(255, 215, 0), "YES") + with imscope.window("Log Management", self.show_windows["Log Management"]) as (exp, opened): + self.show_windows["Log Management"] = bool(opened) + if exp: + if self._log_registry is None: + self._log_registry = log_registry.LogRegistry(str(paths.get_logs_dir() / "log_registry.toml")) else: - imgui.text("NO") - metadata = s_data.get("metadata") or {} - imgui.table_next_column() - imgui.text(metadata.get("reason", "")) - imgui.table_next_column() - imgui.text(str(metadata.get("size_kb", ""))) - imgui.table_next_column() - imgui.text(str(metadata.get("message_count", ""))) - imgui.table_next_column() - if imgui.button(f"Load##{session_id}"): - self.cb_load_prior_log(s_data.get("path")) + if imgui.button("Refresh Registry"): + self._log_registry = log_registry.LogRegistry(str(paths.get_logs_dir() / "log_registry.toml")) + imgui.same_line() + if imgui.button("Load Log"): + self.cb_load_prior_log() imgui.same_line() - if whitelisted: - if imgui.button(f"Unstar##{session_id}"): - registry.update_session_metadata( - session_id, - message_count=int(metadata.get("message_count") or 0), - errors=int(metadata.get("errors") or 0), - size_kb=int(metadata.get("size_kb") or 0), - whitelisted=False, - reason=str(metadata.get("reason") or "") - ) - else: - if imgui.button(f"Star##{session_id}"): - registry.update_session_metadata( - session_id, - message_count=int(metadata.get("message_count") or 0), - errors=int(metadata.get("errors") or 0), - size_kb=int(metadata.get("size_kb") or 0), - whitelisted=True, - reason="Manually whitelisted" - ) - imgui.end_table() + if imgui.button("Force Prune Logs"): + self.controller.event_queue.put("gui_task", {"action": "click", "item": "btn_prune_logs"}) + + registry = self._log_registry + sessions = registry.data + if imgui.begin_table("sessions_table", 7, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable): + imgui.table_setup_column("Session ID") + imgui.table_setup_column("Start Time") + imgui.table_setup_column("Star") + imgui.table_setup_column("Reason") + imgui.table_setup_column("Size (KB)") + imgui.table_setup_column("Msgs") + imgui.table_setup_column("Actions") + imgui.table_headers_row() + for session_id, s_data in sessions.items(): + imgui.table_next_row() + imgui.table_next_column() + imgui.text(session_id) + imgui.table_next_column() + imgui.text(s_data.get("start_time", "")) + imgui.table_next_column() + whitelisted = s_data.get("whitelisted", False) + if whitelisted: + imgui.text_colored(vec4(255, 215, 0), "YES") + else: + imgui.text("NO") + metadata = s_data.get("metadata") or {} + imgui.table_next_column() + imgui.text(metadata.get("reason", "")) + imgui.table_next_column() + imgui.text(str(metadata.get("size_kb", ""))) + imgui.table_next_column() + imgui.text(str(metadata.get("message_count", ""))) + imgui.table_next_column() + if imgui.button(f"Load##{session_id}"): + self.cb_load_prior_log(s_data.get("path")) + imgui.same_line() + if whitelisted: + if imgui.button(f"Unstar##{session_id}"): + registry.update_session_metadata( + session_id, + message_count=int(metadata.get("message_count") or 0), + errors=int(metadata.get("errors") or 0), + size_kb=int(metadata.get("size_kb") or 0), + whitelisted=False, + reason=str(metadata.get("reason") or "") + ) + else: + if imgui.button(f"Star##{session_id}"): + registry.update_session_metadata( + session_id, + message_count=int(metadata.get("message_count") or 0), + errors=int(metadata.get("errors") or 0), + size_kb=int(metadata.get("size_kb") or 0), + whitelisted=True, + reason="Manually whitelisted" + ) + imgui.end_table() if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_log_management") - imgui.end() def _render_diagnostics_panel(self) -> None: if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_diagnostics_panel") - exp, opened = imgui.begin("Diagnostics", self.show_windows.get("Diagnostics", False)) - self.show_windows["Diagnostics"] = bool(opened) - if not exp: - imgui.end() - if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_diagnostics_panel") - return - - metrics = self.perf_monitor.get_metrics() - imgui.text("Performance Telemetry") - imgui.same_line() - _, self.perf_profiling_enabled = imgui.checkbox("Enable Profiling", self.perf_profiling_enabled) - imgui.separator() - - if imgui.begin_table("perf_table", 3, imgui.TableFlags_.borders_inner_h): - imgui.table_setup_column("Metric") - imgui.table_setup_column("Value") - imgui.table_setup_column("Graph") - imgui.table_headers_row() - - for label, key, format_str in [ - ("FPS", "fps", "%.1f"), - ("Frame Time (ms)", "frame_time_ms", "%.2f"), - ("CPU %", "cpu_percent", "%.1f"), - ("Input Lag (ms)", "input_lag_ms", "%.1f") - ]: - imgui.table_next_row() - imgui.table_next_column() - imgui.text(label) - imgui.table_next_column() - if key == "fps": - avg_val = imgui.get_io().framerate - else: - avg_val = metrics.get(f"{key}_avg", metrics.get(key, 0.0)) - imgui.text(format_str % avg_val) - imgui.table_next_column() - self.perf_show_graphs.setdefault(key, False) - _, self.perf_show_graphs[key] = imgui.checkbox(f"##g_{key}", self.perf_show_graphs[key]) - imgui.end_table() - - if self.perf_profiling_enabled: - imgui.separator() - imgui.text("Detailed Component Timings (Moving Average)") - if imgui.begin_table("comp_timings", 6, imgui.TableFlags_.borders): - imgui.table_setup_column("Component") - imgui.table_setup_column("Avg (ms)") - imgui.table_setup_column("Count") - imgui.table_setup_column("Max (ms)") - imgui.table_setup_column("Min (ms)") - imgui.table_setup_column("Graph") - imgui.table_headers_row() - for key, val in metrics.items(): - if key.startswith("time_") and key.endswith("_ms") and not key.endswith("_avg"): - comp_name = key[5:-3] - avg_val = metrics.get(f"{key}_avg", val) - count = int(metrics.get(f"count_{comp_name}", 0)) - max_val = metrics.get(f"max_{comp_name}_ms", 0.0) - min_val = metrics.get(f"min_{comp_name}_ms", 0.0) + with imscope.window("Diagnostics", self.show_windows.get("Diagnostics", False)) as (exp, opened): + self.show_windows["Diagnostics"] = bool(opened) + if exp: + metrics = self.perf_monitor.get_metrics() + imgui.text("Performance Telemetry") + imgui.same_line() + _, self.perf_profiling_enabled = imgui.checkbox("Enable Profiling", self.perf_profiling_enabled) + imgui.separator() + + if imgui.begin_table("perf_table", 3, imgui.TableFlags_.borders_inner_h): + imgui.table_setup_column("Metric") + imgui.table_setup_column("Value") + imgui.table_setup_column("Graph") + imgui.table_headers_row() + + for label, key, format_str in [ + ("FPS", "fps", "%.1f"), + ("Frame Time (ms)", "frame_time_ms", "%.2f"), + ("CPU %", "cpu_percent", "%.1f"), + ("Input Lag (ms)", "input_lag_ms", "%.1f") + ]: imgui.table_next_row() imgui.table_next_column() - imgui.text(comp_name) + imgui.text(label) imgui.table_next_column() - if avg_val > 10.0: - imgui.text_colored(imgui.ImVec4(1.0, 0.2, 0.2, 1.0), f"{avg_val:.2f}") + if key == "fps": + avg_val = imgui.get_io().framerate else: - imgui.text(f"{avg_val:.2f}") + avg_val = metrics.get(f"{key}_avg", metrics.get(key, 0.0)) + imgui.text(format_str % avg_val) imgui.table_next_column() - imgui.text(f"{count}") - imgui.table_next_column() - imgui.text(f"{max_val:.2f}") - imgui.table_next_column() - imgui.text(f"{min_val:.2f}") - imgui.table_next_column() - self.perf_show_graphs.setdefault(comp_name, False) - _, self.perf_show_graphs[comp_name] = imgui.checkbox(f"##g_{comp_name}", self.perf_show_graphs[comp_name]) - imgui.end_table() + self.perf_show_graphs.setdefault(key, False) + _, self.perf_show_graphs[key] = imgui.checkbox(f"##g_{key}", self.perf_show_graphs[key]) + imgui.end_table() - imgui.separator() - imgui.text("Performance Graphs") - for key, show in self.perf_show_graphs.items(): - if show: - imgui.text(f"History: {key}") - hist_data = self.perf_monitor.get_history(key) - if hist_data: - import numpy as np - imgui.plot_lines(f"##plot_{key}", np.array(hist_data, dtype=np.float32), graph_size=imgui.ImVec2(-1, 60)) - else: - imgui.text_disabled(f"(no history data for {key})") + if self.perf_profiling_enabled: + imgui.separator() + imgui.text("Detailed Component Timings (Moving Average)") + if imgui.begin_table("comp_timings", 6, imgui.TableFlags_.borders): + imgui.table_setup_column("Component") + imgui.table_setup_column("Avg (ms)") + imgui.table_setup_column("Count") + imgui.table_setup_column("Max (ms)") + imgui.table_setup_column("Min (ms)") + imgui.table_setup_column("Graph") + imgui.table_headers_row() + for key, val in metrics.items(): + if key.startswith("time_") and key.endswith("_ms") and not key.endswith("_avg"): + comp_name = key[5:-3] + avg_val = metrics.get(f"{key}_avg", val) + count = int(metrics.get(f"count_{comp_name}", 0)) + max_val = metrics.get(f"max_{comp_name}_ms", 0.0) + min_val = metrics.get(f"min_{comp_name}_ms", 0.0) + imgui.table_next_row() + imgui.table_next_column() + imgui.text(comp_name) + imgui.table_next_column() + if avg_val > 10.0: + imgui.text_colored(imgui.ImVec4(1.0, 0.2, 0.2, 1.0), f"{avg_val:.2f}") + else: + imgui.text(f"{avg_val:.2f}") + imgui.table_next_column() + imgui.text(f"{count}") + imgui.table_next_column() + imgui.text(f"{max_val:.2f}") + imgui.table_next_column() + imgui.text(f"{min_val:.2f}") + imgui.table_next_column() + self.perf_show_graphs.setdefault(comp_name, False) + _, self.perf_show_graphs[comp_name] = imgui.checkbox(f"##g_{comp_name}", self.perf_show_graphs[comp_name]) + imgui.end_table() - imgui.separator() - imgui.text("Diagnostic Log") - if imgui.begin_table("diag_log_table", 3, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable): - imgui.table_setup_column("Timestamp", imgui.TableColumnFlags_.width_fixed, 150) - imgui.table_setup_column("Type", imgui.TableColumnFlags_.width_fixed, 100) - imgui.table_setup_column("Message") - imgui.table_headers_row() - for entry in reversed(self.controller.diagnostic_log): - imgui.table_next_row() - imgui.table_next_column() - imgui.text(entry.get("ts", "")) - imgui.table_next_column() - imgui.text(entry.get("type", "")) - imgui.table_next_column() - imgui.text_wrapped(entry.get("message", "")) - imgui.end_table() + imgui.separator() + imgui.text("Performance Graphs") + for key, show in self.perf_show_graphs.items(): + if show: + imgui.text(f"History: {key}") + hist_data = self.perf_monitor.get_history(key) + if hist_data: + import numpy as np + imgui.plot_lines(f"##plot_{key}", np.array(hist_data, dtype=np.float32), graph_size=imgui.ImVec2(-1, 60)) + else: + imgui.text_disabled(f"(no history data for {key})") + + imgui.separator() + imgui.text("Diagnostic Log") + if imgui.begin_table("diag_log_table", 3, imgui.TableFlags_.borders | imgui.TableFlags_.row_bg | imgui.TableFlags_.resizable): + imgui.table_setup_column("Timestamp", imgui.TableColumnFlags_.width_fixed, 150) + imgui.table_setup_column("Type", imgui.TableColumnFlags_.width_fixed, 100) + imgui.table_setup_column("Message") + imgui.table_headers_row() + for entry in reversed(self.controller.diagnostic_log): + imgui.table_next_row() + imgui.table_next_column() + imgui.text(entry.get("ts", "")) + imgui.table_next_column() + imgui.text(entry.get("type", "")) + imgui.table_next_column() + imgui.text_wrapped(entry.get("message", "")) + imgui.end_table() if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_diagnostics_panel") - imgui.end() def _render_discussion_tab(self) -> None: imgui.begin_child("HistoryChild", size=(0, -self.ui_discussion_split_h)) @@ -3215,118 +3202,116 @@ class App: grouped_files = aggregate.group_files_by_dir(self.context_files) - if imgui.begin_table("ctx_comp_table", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders): - imgui.table_setup_column("File", imgui.TableColumnFlags_.width_stretch) - imgui.table_setup_column("Flags", imgui.TableColumnFlags_.width_fixed, 200) - imgui.table_headers_row() - - file_indices = {id(f): idx for idx, f in enumerate(self.context_files)} - - for dir_name, g_files in grouped_files.items(): - imgui.table_next_row() - imgui.table_set_column_index(0) - is_open = imgui.tree_node_ex(f"{dir_name}##dir_{dir_name}", imgui.TreeNodeFlags_.default_open) - imgui.table_set_column_index(1) - if is_open: - for f_item in g_files: - i = file_indices[id(f_item)] - imgui.table_next_row() - imgui.table_set_column_index(0) - - # Checkbox for selection - f_path = f_item.path if hasattr(f_item, "path") else str(f_item) - is_sel = f_path in self.ui_selected_context_files - changed_sel, is_sel = imgui.checkbox(f"##sel{i}", is_sel) - if changed_sel: - if imgui.get_io().key_shift and self._last_selected_context_index != -1: - start = min(self._last_selected_context_index, i) - end = max(self._last_selected_context_index, i) - for idx in range(start, end + 1): - item = self.context_files[idx] - item_path = item.path if hasattr(item, "path") else str(item) - if is_sel: - self.ui_selected_context_files.add(item_path) - else: - self.ui_selected_context_files.discard(item_path) - else: - if is_sel: - self.ui_selected_context_files.add(f_path) - else: - self.ui_selected_context_files.discard(f_path) - self._last_selected_context_index = i - imgui.same_line() - - mtime = os.path.getmtime(f_path) if os.path.exists(f_path) else 0 - cache_key = f"{f_path}_{mtime}" - stats = self._file_stats_cache.get(cache_key, {"lines": 0, "ast_elements": 0}) - f_name = os.path.basename(f_path) - imgui.text(f"{f_name} (L: {stats.get('lines', 0)}, AST: {stats.get('ast_elements', 0)})") - - if f_path.lower().endswith(('.c', '.cpp', '.h', '.hpp', '.cxx', '.cc')): - imgui.same_line() - if imgui.button(f"[Inspect]##{i}"): - self.ui_inspecting_ast_file = f_item - self._show_ast_inspector = True - - imgui.same_line() - if imgui.button(f"[Slices]##{i}"): - self.ui_editing_slices_file = f_item - f_path = f_item.path if hasattr(f_item, "path") else str(f_item) - self.text_viewer_title = f"Slices: {f_path}" - try: - self.text_viewer_content = mcp_client.read_file(f_path) - except Exception as e: - self.text_viewer_content = f"Error reading file: {e}" - self.text_viewer_type = 'cpp' if f_path.endswith(('.cpp', '.hpp', '.h')) else 'python' if f_path.endswith('.py') else 'text' - self.show_text_viewer = True - + with imscope.table("ctx_comp_table", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders) as active: + if active: + imgui.table_setup_column("File", imgui.TableColumnFlags_.width_stretch) + imgui.table_setup_column("Flags", imgui.TableColumnFlags_.width_fixed, 200) + imgui.table_headers_row() + + file_indices = {id(f): idx for idx, f in enumerate(self.context_files)} + + for dir_name, g_files in grouped_files.items(): + imgui.table_next_row() + imgui.table_set_column_index(0) + with imscope.tree_node_ex(f"{dir_name}##dir_{dir_name}", imgui.TreeNodeFlags_.default_open) as is_open: imgui.table_set_column_index(1) - if not hasattr(f_item, "view_mode"): - f_item.view_mode = "summary" - view_modes = ["full", "summary", "skeleton", "outline", "masked", "none"] - try: - current_idx = view_modes.index(f_item.view_mode) - except ValueError: - current_idx = 1 - f_item.view_mode = "summary" - imgui.set_next_item_width(120) - changed_vm, new_idx = imgui.combo(f"##vm{i}", current_idx, view_modes) - if changed_vm: - f_item.view_mode = view_modes[new_idx] - - imgui.same_line() - if imgui.button(f"[Save]##vpsave{i}"): - imgui.open_popup(f"save_vp_popup{i}") - - if imgui.begin_popup(f"save_vp_popup{i}"): - imgui.text("Preset Name:") - changed_pname, self.ui_new_vp_name = imgui.input_text(f"##pname{i}", self.ui_new_vp_name) - if imgui.button("OK"): - if self.ui_new_vp_name.strip(): - self.controller._cb_save_view_preset(self.ui_new_vp_name.strip(), f_item) - self.ui_new_vp_name = "" - imgui.close_current_popup() - imgui.end_popup() + if is_open: + for f_item in g_files: + i = file_indices[id(f_item)] + imgui.table_next_row() + imgui.table_set_column_index(0) + + # Checkbox for selection + f_path = f_item.path if hasattr(f_item, "path") else str(f_item) + is_sel = f_path in self.ui_selected_context_files + changed_sel, is_sel = imgui.checkbox(f"##sel{i}", is_sel) + if changed_sel: + if imgui.get_io().key_shift and self._last_selected_context_index != -1: + start = min(self._last_selected_context_index, i) + end = max(self._last_selected_context_index, i) + for idx in range(start, end + 1): + item = self.context_files[idx] + item_path = item.path if hasattr(item, "path") else str(item) + if is_sel: + self.ui_selected_context_files.add(item_path) + else: + self.ui_selected_context_files.discard(item_path) + else: + if is_sel: + self.ui_selected_context_files.add(f_path) + else: + self.ui_selected_context_files.discard(f_path) + self._last_selected_context_index = i + imgui.same_line() + + mtime = os.path.getmtime(f_path) if os.path.exists(f_path) else 0 + cache_key = f"{f_path}_{mtime}" + stats = self._file_stats_cache.get(cache_key, {"lines": 0, "ast_elements": 0}) + f_name = os.path.basename(f_path) + imgui.text(f"{f_name} (L: {stats.get('lines', 0)}, AST: {stats.get('ast_elements', 0)})") - imgui.same_line() - if imgui.button(f"[Load]##vpload{i}"): - imgui.open_popup(f"load_vp_popup{i}") - - if imgui.begin_popup(f"load_vp_popup{i}"): - vp_names = sorted([vp.name for vp in self.controller.view_presets]) - if not vp_names: - imgui.text("No presets saved.") - for vp_name in vp_names: - if imgui.selectable(vp_name): - self.controller._cb_apply_view_preset(vp_name, f_item) - imgui.close_current_popup() - imgui.end_popup() - if hasattr(f_item, "custom_slices") and f_item.custom_slices: - imgui.same_line() - imgui.text_colored(imgui.ImVec4(1.0, 0.5, 0.0, 1.0), "[Slices Active]") - imgui.tree_pop() - - imgui.end_table() + if f_path.lower().endswith(('.c', '.cpp', '.h', '.hpp', '.cxx', '.cc')): + imgui.same_line() + if imgui.button(f"[Inspect]##{i}"): + self.ui_inspecting_ast_file = f_item + self._show_ast_inspector = True + + imgui.same_line() + if imgui.button(f"[Slices]##{i}"): + self.ui_editing_slices_file = f_item + f_path = f_item.path if hasattr(f_item, "path") else str(f_item) + self.text_viewer_title = f"Slices: {f_path}" + try: + self.text_viewer_content = mcp_client.read_file(f_path) + except Exception as e: + self.text_viewer_content = f"Error reading file: {e}" + self.text_viewer_type = 'cpp' if f_path.endswith(('.cpp', '.hpp', '.h')) else 'python' if f_path.endswith('.py') else 'text' + self.show_text_viewer = True + + imgui.table_set_column_index(1) + if not hasattr(f_item, "view_mode"): + f_item.view_mode = "summary" + view_modes = ["full", "summary", "skeleton", "outline", "masked", "none"] + try: + current_idx = view_modes.index(f_item.view_mode) + except ValueError: + current_idx = 1 + f_item.view_mode = "summary" + imgui.set_next_item_width(120) + changed_vm, new_idx = imgui.combo(f"##vm{i}", current_idx, view_modes) + if changed_vm: + f_item.view_mode = view_modes[new_idx] + + imgui.same_line() + if imgui.button(f"[Save]##vpsave{i}"): + imgui.open_popup(f"save_vp_popup{i}") + + if imgui.begin_popup(f"save_vp_popup{i}"): + imgui.text("Preset Name:") + changed_pname, self.ui_new_vp_name = imgui.input_text(f"##pname{i}", self.ui_new_vp_name) + if imgui.button("OK"): + if self.ui_new_vp_name.strip(): + self.controller._cb_save_view_preset(self.ui_new_vp_name.strip(), f_item) + self.ui_new_vp_name = "" + imgui.close_current_popup() + imgui.end_popup() + + imgui.same_line() + if imgui.button(f"[Load]##vpload{i}"): + imgui.open_popup(f"load_vp_popup{i}") + + if imgui.begin_popup(f"load_vp_popup{i}"): + vp_names = sorted([vp.name for vp in self.controller.view_presets]) + if not vp_names: + imgui.text("No presets saved.") + for vp_name in vp_names: + if imgui.selectable(vp_name): + self.controller._cb_apply_view_preset(vp_name, f_item) + imgui.close_current_popup() + imgui.end_popup() + if hasattr(f_item, "custom_slices") and f_item.custom_slices: + imgui.same_line() + imgui.text_colored(imgui.ImVec4(1.0, 0.5, 0.0, 1.0), "[Slices Active]") # Context Composition collasping header imgui.separator() @@ -4078,53 +4063,47 @@ def hello(): self._handle_reset_session() if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_message_panel") - def _render_response_panel(self) -> None: - if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_response_panel") - if self._trigger_blink: - self._trigger_blink = False - self._is_blinking = True - self._blink_start_time = time.time() - try: - imgui.set_window_focus("Response") # type: ignore[call-arg] - except: - pass - is_blinking = False - if self._is_blinking: - elapsed = time.time() - self._blink_start_time - if elapsed > 1.5: - self._is_blinking = False - else: - is_blinking = True - val = math.sin(elapsed * 8 * math.pi) - alpha = 50/255 if val > 0 else 0 - imgui.push_style_color(imgui.Col_.frame_bg, vec4(0, 255, 0, alpha)) - imgui.push_style_color(imgui.Col_.child_bg, vec4(0, 255, 0, alpha)) - # --- Always Render Content --- - - imgui.begin_child("response_scroll_area", imgui.ImVec2(0, -40), True) - is_nerv = theme.is_nerv_active() - if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80)) - - segments, parsed_response = thinking_parser.parse_thinking_trace(self.ai_response) - if segments: - self._render_thinking_trace([{"content": s.content, "marker": s.marker} for s in segments], 9999) - - markdown_helper.render(parsed_response, context_id="response") - - if is_nerv: imgui.pop_style_color() - imgui.end_child() - - imgui.separator() - if imgui.button("-> History"): - if self.ai_response: - segments, response = thinking_parser.parse_thinking_trace(self.ai_response) - entry = {"role": "AI", "content": response, "collapsed": True, "ts": project_manager.now_ts()} - if segments: - entry["thinking_segments"] = [{"content": s.content, "marker": s.marker} for s in segments] - self.disc_entries.append(entry) - if is_blinking: - imgui.pop_style_color(2) - if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_response_panel") + def _render_response_panel(self) -> None: + if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_response_panel") + if self._trigger_blink: + self._trigger_blink = False + self._is_blinking = True + self._blink_start_time = time.time() + try: + imgui.set_window_focus("Response") # type: ignore[call-arg] + except: + pass + is_blinking = False + blink_color = vec4(0, 0, 0, 0) + if self._is_blinking: + elapsed = time.time() - self._blink_start_time + if elapsed > 1.5: + self._is_blinking = False + else: + 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) + + 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(): + with imscope.child("response_scroll_area", imgui.ImVec2(0, -40), True): + is_nerv = theme.is_nerv_active() + with imscope.style_color(imgui.Col_.text, vec4(80, 255, 80)) if is_nerv else nullcontext(): + segments, parsed_response = thinking_parser.parse_thinking_trace(self.ai_response) + if segments: + self._render_thinking_trace([{"content": s.content, "marker": s.marker} for s in segments], 9999) + markdown_helper.render(parsed_response, context_id="response") + + imgui.separator() + if imgui.button("-> History"): + if self.ai_response: + segments, response = thinking_parser.parse_thinking_trace(self.ai_response) + entry = {"role": "AI", "content": response, "collapsed": True, "ts": project_manager.now_ts()} + if segments: + entry["thinking_segments"] = [{"content": s.content, "marker": s.marker} for s in segments] + self.disc_entries.append(entry) + if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_response_panel") def _render_external_tools_panel(self) -> None: if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_external_tools_panel") @@ -5188,34 +5167,6 @@ def hello(): imgui.end() if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_theme_panel") - 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: diff --git a/src/imgui_scopes.py b/src/imgui_scopes.py index 3fb3f57..506b5fe 100644 --- a/src/imgui_scopes.py +++ b/src/imgui_scopes.py @@ -160,3 +160,39 @@ class _ScopeTabItem: if self._expanded: imgui.end_tab_item() return False + +def style_color(col: int, val: Any): return _ScopeStyleColor(col, val) +class _ScopeStyleColor: + def __init__(self, col: int, val: Any): + self._col = col + self._val = val + def __enter__(self): + imgui.push_style_color(self._col, self._val) + def __exit__(self, *args): + imgui.pop_style_color() + return False + +def style_var(var: int, val: Any): return _ScopeStyleVar(var, val) +class _ScopeStyleVar: + def __init__(self, var: int, val: Any): + self._var = var + self._val = val + def __enter__(self): + imgui.push_style_var(self._var, self._val) + def __exit__(self, *args): + imgui.pop_style_var() + return False + +def tree_node_ex(label: str, flags: int = 0): return _ScopeTreeNodeEx(label, flags) +class _ScopeTreeNodeEx: + def __init__(self, label: str, flags: int): + self._label = label + self._flags = flags + self._opened = False + def __enter__(self): + self._opened = imgui.tree_node_ex(self._label, self._flags) + return self._opened + def __exit__(self, *args): + if self._opened: + imgui.tree_pop() + return False