From 394020e50c313502356f1dec81f68cebf8557d9b Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 13 Jun 2026 10:27:48 -0400 Subject: [PATCH] reading throuogh gui_2.py (still reading) --- src/gui_2.py | 835 ++++++++++++++++++++++----------------------------- 1 file changed, 363 insertions(+), 472 deletions(-) diff --git a/src/gui_2.py b/src/gui_2.py index a79c4bb0..c852a24b 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -311,7 +311,7 @@ class App: with startup_profiler.phase("app_init_history_perfmon"): from src import history, performance_monitor self.perf_monitor = performance_monitor.PerformanceMonitor() - self.history = history.HistoryManager(max_capacity=100) + self.history = history.HistoryManager(max_capacity=100) # --- Undo/Redo & Snapshot State --- self._last_ui_snapshot: Optional[history.UISnapshot] = None @@ -369,6 +369,7 @@ class App: self.controller.save_context_preset(preset) self.ui_new_context_preset_name = "" self.show_missing_files_modal = False + self.controller._predefined_callbacks['get_app_debug_info'] = lambda: self.app_debug_info self.controller._gettable_fields ['app_debug_info'] = 'app_debug_info' self.controller._predefined_callbacks['save_context_preset_force'] = _save_context_preset_force @@ -519,28 +520,29 @@ class App: self.ui_synthesis_selected_takes: dict[str, bool] = {} # --- Rendering & Theme State --- - self.perf_show_graphs: dict[str, bool] = {} - self.ui_crt_filter = False - self.ui_tool_filter_category = "All" - self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8} - self._is_generating_preview = False - self._pending_preview_refresh = False - self._preview_refresh_timer: float = 0.0 + self.perf_show_graphs:dict[str, bool] = {} + self.ui_crt_filter = False + self.ui_tool_filter_category = "All" + self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8} + self._is_generating_preview = False + self._pending_preview_refresh = False + self._preview_refresh_timer: float = 0.0 self._preview_refresh_debounce: float = 0.5 self._hot_reload_error: Optional[str] = None def _set_context_files(self, paths: list[str]) -> None: from src import models - self.context_files = [models.FileItem(path=p) for p in paths] + self.context_files = [models.FileItem(path=p) for p in paths] self.controller.context_files = self.context_files def _simulate_save_preset(self, name: str) -> None: from src import models - item = models.FileItem(path='test.py') - self.files = [item] + item = models.FileItem(path='test.py') + self.files = [item] self.context_files = [item] - self.screenshots = ['test.png'] + self.screenshots = ['test.png'] self.save_context_preset(name) + def _handle_approve_ask(self) -> None: """UI-level wrapper for approving a pending tool execution ask.""" self.controller._handle_approve_ask() @@ -590,8 +592,8 @@ class App: sys.stderr.write(f"[GUI] could not read layout file: {_e}\n") return _STALE_WINDOW_NAMES = { - "Projects", "Files", "Screenshots", "Discussion History", - "Provider", "Message", "Response", "Tool Calls", + "Projects", "Files", "Screenshots", "Discussion History", + "Provider", "Message", "Response", "Tool Calls", "Comms History", "System Prompts", } _stale_found = [n for n in _STALE_WINDOW_NAMES if f"[Window][{n}]" in _ini_text] @@ -601,7 +603,7 @@ class App: def _trigger_hot_reload(self) -> bool: from src.hot_reloader import HotReloader - result = HotReloader.reload_all(self) + result = HotReloader.reload_all(self) self._hot_reload_error = HotReloader.last_error return result @@ -615,8 +617,8 @@ class App: self._fetch_models(self.current_provider) import uvicorn headless_cfg = self.config.get("headless", {}) - port = headless_cfg.get("port", 8000) - api = self.create_api() + port = headless_cfg.get("port", 8000) + api = self.create_api() uvicorn.run(api, host="0.0.0.0", port=port) else: from src.startup_profiler import startup_profiler @@ -957,15 +959,12 @@ class App: """Re-applies the next snapshot in the history stack (forward navigation). SSDL Shape: `[I:snapshot] -> [B:history] => [I:state]` """ - sys.stderr.write(f"[DEBUG History] _handle_redo called. can_redo={self.history.can_redo}\n") - sys.stderr.flush() - if not self.history.can_redo: - return + sys.stderr.write(f"[DEBUG History] _handle_redo called. can_redo={self.history.can_redo}\n"); sys.stderr.flush() + if not self.history.can_redo: return current = self._take_snapshot() - entry = self.history.redo(current, "Redo Action") + entry = self.history.redo(current, "Redo Action") if entry: - sys.stderr.write(f"[DEBUG History] Redoing to: {entry.description}\n") - sys.stderr.flush() + sys.stderr.write(f"[DEBUG History] Redoing to: {entry.description}\n"); sys.stderr.flush() self._apply_snapshot(entry.state) def shutdown(self) -> None: @@ -973,6 +972,7 @@ class App: forces a save of dirty registries/caches, and terminates the active thread pools. SSDL Shape: `[I:save_ini] -> [I:controller_shutdown]` """ + #Note(Ed): Exception(Thirdparty) try: if hasattr(self, 'runner_params') and self.runner_params.ini_filename: imgui.save_ini_settings_to_disk(self.runner_params.ini_filename) @@ -1218,7 +1218,7 @@ class App: win32gui.SendMessage(hwnd, win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0) imgui.push_style_color(imgui.Col_.button, imgui.ImVec4(0, 0, 0, 0)) - #Note(Ed): Thirdparty Exception + #Note(Ed): Exception(Thirdparty) try: is_max = win32gui.GetWindowPlacement(hwnd)[1] == win32con.SW_SHOWMAXIMIZED except Exception: is_max = False # Explicitly set Y to 0 and match button height to bar height for perfect alignment @@ -1246,19 +1246,19 @@ class App: # Compare only core fields for performance changed = ( - current.ai_input != self._last_ui_snapshot.ai_input or - current.project_system_prompt != self._last_ui_snapshot.project_system_prompt or - current.global_system_prompt != self._last_ui_snapshot.global_system_prompt or - current.base_system_prompt != self._last_ui_snapshot.base_system_prompt or + current.ai_input != self._last_ui_snapshot.ai_input or + current.project_system_prompt != self._last_ui_snapshot.project_system_prompt or + current.global_system_prompt != self._last_ui_snapshot.global_system_prompt or + current.base_system_prompt != self._last_ui_snapshot.base_system_prompt or current.use_default_base_prompt != self._last_ui_snapshot.use_default_base_prompt or - abs(current.temperature - self._last_ui_snapshot.temperature) > 1e-5 or - abs(current.top_p - self._last_ui_snapshot.top_p) > 1e-5 or - current.max_tokens != self._last_ui_snapshot.max_tokens or - current.auto_add_history != self._last_ui_snapshot.auto_add_history or - len(current.disc_entries) != len(self._last_ui_snapshot.disc_entries) or - len(current.files) != len(self._last_ui_snapshot.files) or - len(current.context_files) != len(self._last_ui_snapshot.context_files) or - len(current.screenshots) != len(self._last_ui_snapshot.screenshots) + abs(current.temperature - self._last_ui_snapshot.temperature) > 1e-5 or + abs(current.top_p - self._last_ui_snapshot.top_p) > 1e-5 or + current.max_tokens != self._last_ui_snapshot.max_tokens or + current.auto_add_history != self._last_ui_snapshot.auto_add_history or + len(current.disc_entries) != len(self._last_ui_snapshot.disc_entries) or + len(current.files) != len(self._last_ui_snapshot.files) or + len(current.context_files) != len(self._last_ui_snapshot.context_files) or + len(current.screenshots) != len(self._last_ui_snapshot.screenshots) ) if not changed and len(current.disc_entries) > 0: @@ -1270,7 +1270,7 @@ class App: self._pending_snapshot = True self._snapshot_timer = time.time() # Capture state BEFORE current change - self._state_to_push = self._last_ui_snapshot + self._state_to_push = self._last_ui_snapshot else: # Reset timer for settle debounce self._snapshot_timer = time.time() @@ -1293,8 +1293,7 @@ class App: root = hide_tk_root() path = filedialog.askdirectory(title='Select Session Directory', initialdir=str(paths.get_logs_dir())) root.destroy() - if path: - self.controller.cb_load_prior_log(path) + if path: self.controller.cb_load_prior_log(path) def _set_external_editor_default(self, editor_name: str) -> None: from src import models @@ -1306,7 +1305,7 @@ class App: def _save_paths(self) -> None: self.config["paths"] = { - "logs_dir": self.ui_logs_dir, + "logs_dir": self.ui_logs_dir, "scripts_dir": self.ui_scripts_dir } cfg_path = paths.get_config_path() @@ -1326,9 +1325,11 @@ class App: mcp_client.configure([{"path": abs_path}], [proj_dir] if proj_dir else None) f_path_lower = f_item.path.lower() + + #TODO(Ed): Exception(Review) try: - if f_path_lower.endswith('.py'): outline = mcp_client.py_get_code_outline(abs_path) - elif f_path_lower.endswith(('.c', '.h')): outline = mcp_client.ts_c_get_code_outline(abs_path) + if f_path_lower.endswith('.py'): outline = mcp_client.py_get_code_outline(abs_path) + elif f_path_lower.endswith(('.c', '.h')): outline = mcp_client.ts_c_get_code_outline(abs_path) elif f_path_lower.endswith(('.cpp', '.hpp', '.cxx', '.cc')): outline = mcp_client.ts_cpp_get_code_outline(abs_path) else: return except Exception: @@ -1342,6 +1343,7 @@ class App: text = f.read() except Exception: return + #TODO(Ed): Exception(Review) try: from src.fuzzy_anchor import FuzzyAnchor except ImportError: @@ -1352,11 +1354,9 @@ class App: e_line = int(e_str) if any(s.get('start_line') == s_line and s.get('end_line') == e_line for s in f_item.custom_slices): continue - if FuzzyAnchor: - slice_data = FuzzyAnchor.create_slice(text, s_line, e_line) - else: - slice_data = {"start_line": s_line, "end_line": e_line} - slice_data['tag'] = 'auto-ast' + if FuzzyAnchor: slice_data = FuzzyAnchor.create_slice(text, s_line, e_line) + else: slice_data = {"start_line": s_line, "end_line": e_line} + slice_data['tag'] = 'auto-ast' slice_data['comment'] = name f_item.custom_slices.append(slice_data) @@ -1402,12 +1402,13 @@ class App: if not self._pending_patch_text: self._patch_error_message = "No patch to apply" return + #TODO(Ed): Exception(Review) try: - base_dir = str(self.controller.current_project_dir) if hasattr(self.controller, 'current_project_dir') else "." + base_dir = str(self.controller.current_project_dir) if hasattr(self.controller, 'current_project_dir') else "." success, msg = apply_patch_to_file(self._pending_patch_text, base_dir) if success: - self._show_patch_modal = False - self._pending_patch_text = None + self._show_patch_modal = False + self._pending_patch_text = None self._pending_patch_files = [] self._patch_error_message = None imgui.close_current_popup() @@ -1425,7 +1426,7 @@ class App: self._patch_error_message = "No files to edit" return launcher = get_default_launcher(self.config) - editor = launcher.config.get_default() + editor = launcher.config.get_default() if not editor: self._patch_error_message = "No external editor configured" return @@ -1434,7 +1435,7 @@ class App: self._patch_error_message = f"Original file not found: {original_path}" return temp_path = create_temp_modified_file(self._pending_patch_text) - result = launcher.launch_diff(None, original_path, temp_path) + result = launcher.launch_diff(None, original_path, temp_path) if result is None: self._patch_error_message = "Failed to launch external editor" else: self._patch_error_message = None @@ -1467,11 +1468,11 @@ class App: from src.diff_viewer import parse_diff patch_text = ai_client.run_tier4_patch_generation(error, file_context) if patch_text and "---" in patch_text and "+++" in patch_text: - diff_files = parse_diff(patch_text) - file_paths = [df.old_path for df in diff_files] - self._pending_patch_text = patch_text + diff_files = parse_diff(patch_text) + file_paths = [df.old_path for df in diff_files] + self._pending_patch_text = patch_text self._pending_patch_files = file_paths - self._show_patch_modal = True + self._show_patch_modal = True else: self._patch_error_message = patch_text or "No patch generated" except Exception as e: @@ -1549,25 +1550,23 @@ if __name__ == "__main__": main() def render_main_interface(app: App) -> None: - """ - Top-level per-frame orchestrator. Dispatches every subsystem in the correct order: + """Top-level per-frame orchestrator. Dispatches every subsystem in the correct order: error/stale overlay tints, perf bookends, GUI task draining, modal rendering, auto-save, comms/tool-log caching, and all dockable windows. - SSDL Shape: - `[I:overlays] -> [I:task_drain] -> [I:modals] -> [I:windows] -> [I:popups]` + SSDL: `[I:overlays] -> [I:task_drain] -> [I:modals] -> [I:windows] -> [I:popups]` ASCII Layout Map: Full-screen dockspace (managed by imgui_bundle): - +-------------------+------------------------------------+ - | Project Settings | Discussion Hub | - | AI Settings | +--------------------------------+ | - | Files & Media | | History entries (scrollable) | | - | Usage Analytics | +--------------------------------+ | - | MMA Dashboard | [splitter] | - | Task DAG | [Message] [Response] | - | Tier 1..4 streams | | - +-------------------+------------------+-----------------+ + +-------------------+-------------------------------------+ + | Project Settings | Discussion Hub | + | AI Settings | +--------------------------------+ | + | Files & Media | | History entries (scrollable) | | + | Usage Analytics | +--------------------------------+ | + | MMA Dashboard | [splitter] | + | Task DAG | [Message] [Response] | + | Tier 1..4 streams | | + +-------------------+------------------+------------------+ | Operations Hub (tab-bar) | Theme window | | [Comms][Tools][Usage][Ext][Layouts] | | +---------------------------------------+-----------------+ @@ -1594,12 +1593,13 @@ def render_main_interface(app: App) -> None: render_save_workspace_profile_modal(app) render_add_context_files_modal(app) from src.command_palette import render_palette_modal - from src.commands import registry as _cmd_registry + from src.commands import registry as _cmd_registry render_palette_modal(app, _cmd_registry.all()) render_preset_manager_window(app) render_tool_preset_manager_window(app) render_persona_editor_window(app) + #TODO(Ed): Exception(Review) # Auto-save (every 60s) now = time.time() if now - app._last_autosave >= app._autosave_interval: @@ -1629,7 +1629,7 @@ def render_main_interface(app: App) -> None: else: log_raw = list(app._comms_log) if app.ui_focus_agent: app._comms_log_cache = [e for e in log_raw if e.get("source_tier", "").startswith(app.ui_focus_agent)] - else: app._comms_log_cache = log_raw + else: app._comms_log_cache = log_raw app._comms_log_dirty = False if app._tool_log_dirty: @@ -1637,7 +1637,7 @@ def render_main_interface(app: App) -> None: else: log_raw = list(app._tool_log) if app.ui_focus_agent: app._tool_log_cache = [e for e in log_raw if e.get("source_tier", "").startswith(app.ui_focus_agent)] - else: app._tool_log_cache = log_raw + else: app._tool_log_cache = log_raw app._tool_log_dirty = False app._render_window_if_open("Project Settings", lambda: render_project_settings_hub(app)) @@ -1645,30 +1645,28 @@ def render_main_interface(app: App) -> None: app._render_window_if_open("AI Settings", lambda: render_ai_settings_hub(app)) app._render_window_if_open("Usage Analytics", lambda: render_usage_analytics_panel(app), app.ui_separate_usage_analytics) app._render_window_if_open("MMA Dashboard", lambda: render_mma_dashboard(app)) - app._render_window_if_open("Task DAG", lambda: render_task_dag_panel(app), app.ui_separate_task_dag) + app._render_window_if_open("Task DAG", lambda: render_task_dag_panel(app), app.ui_separate_task_dag) - app._render_window_if_open("Tier 1: Strategy", lambda: render_tier_stream_panel(app, "Tier 1", "Tier 1"), app.ui_separate_tier1) + app._render_window_if_open("Tier 1: Strategy", lambda: render_tier_stream_panel(app, "Tier 1", "Tier 1"), app.ui_separate_tier1) app._render_window_if_open("Tier 2: Tech Lead", lambda: render_tier_stream_panel(app, "Tier 2", "Tier 2 (Tech Lead)"), app.ui_separate_tier2) - app._render_window_if_open("Tier 3: Workers", lambda: render_tier_stream_panel(app, "Tier 3", None), app.ui_separate_tier3) - app._render_window_if_open("Tier 4: QA", lambda: render_tier_stream_panel(app, "Tier 4", "Tier 4 (QA)"), app.ui_separate_tier4) + app._render_window_if_open("Tier 3: Workers", lambda: render_tier_stream_panel(app, "Tier 3", None), app.ui_separate_tier3) + app._render_window_if_open("Tier 4: QA", lambda: render_tier_stream_panel(app, "Tier 4", "Tier 4 (QA)"), app.ui_separate_tier4) if app.show_windows.get("Theme", False): render_theme_panel(app) app._render_window_if_open("Discussion Hub", lambda: render_discussion_hub(app)) app._render_window_if_open("Operations Hub", lambda: render_operations_hub(app)) - app._render_window_if_open("Message", lambda: render_message_panel(app), app.ui_separate_message_panel) - app._render_window_if_open("Response", lambda: render_response_panel(app), app.ui_separate_response_panel) - app._render_window_if_open("Tool Calls", lambda: render_tool_calls_panel(app), app.ui_separate_tool_calls_panel) + app._render_window_if_open("Message", lambda: render_message_panel(app), app.ui_separate_message_panel) + app._render_window_if_open("Response", lambda: render_response_panel(app), app.ui_separate_response_panel) + app._render_window_if_open("Tool Calls", lambda: render_tool_calls_panel(app), app.ui_separate_tool_calls_panel) app._render_window_if_open("External Tools", lambda: render_external_tools_panel(app), app.ui_separate_external_tools) app._render_window_if_open("Log Management", lambda: render_log_management(app)) app._render_window_if_open("Diagnostics", lambda: render_diagnostics_panel(app)) app._render_window_if_open("Context Preview", lambda: render_context_preview_window(app)) render_text_viewer_window(app) - app.perf_monitor.end_frame() - # Modals / Popups render_approve_script_modal(app) @@ -1683,12 +1681,10 @@ def render_custom_title_bar(app: App) -> None: #region: Diagnostics & Analytics def render_usage_analytics_panel(app: App) -> None: - """ - Renders the aggregate dashboard panel for usage, budgeting, cache analytics, + """Renders the aggregate dashboard panel for usage, budgeting, cache analytics, tool performance metrics, session insights, and RAG status. - SSDL Shape: - `[I:token_budget] -> [I:cache_panel] -> [I:tool_analytics] -> [I:session_insights] -> [I:rag_status]` + SSDL:`[I:token_budget] -> [I:cache_panel] -> [I:tool_analytics] -> [I:session_insights] -> [I:rag_status]` ASCII Layout Map: +---------------------------------------------------------+ @@ -1728,31 +1724,29 @@ def render_usage_analytics_panel(app: App) -> None: if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_usage_analytics_panel") def render_diagnostics_panel(app: App) -> None: - """ - Renders the Diagnostics window. Shows FPS, frame time, CPU%, and input lag in a + """Renders the Diagnostics window. Shows FPS, frame time, CPU%, and input lag in a summary table; when profiling is on, shows per-component moving-average timings, optional sparkline graphs, and the diagnostic message log. - SSDL Shape: - `[I:perf_summary_table] -> [B:enable_profiling] => [I:component_timings_table] -> [I:graphs] -> [I:diag_log]` + SSDL: `[I:perf_summary_table] -> [B:enable_profiling] => [I:component_timings_table] -> [I:graphs] -> [I:diag_log]` ASCII Layout Map: +---------------------------------------------------------+ | Performance Telemetry [ ] Enable Profiling | - | +----------------+---------+-------+ | - | | FPS | 120.0 | [ ] | | - | | Frame Time(ms) | 8.33 | [ ] | | - | | CPU % | 4.2 | [ ] | | - | +----------------+---------+-------+ | + | +----------------+---------+-------+ | + | | FPS | 120.0 | [ ] | | + | | Frame Time(ms) | 8.33 | [ ] | | + | | CPU % | 4.2 | [ ] | | + | +----------------+---------+-------+ | | (when profiling enabled) | | Detailed Component Timings | - | +--------------------+------+-----+------+------+----+ | - | | Component | Avg | Cnt | Max | Min | G | | - | +--------------------+------+-----+------+------+----+ | + | +--------------------+------+-----+------+------+----+ | + | | Component | Avg | Cnt | Max | Min | G | | + | +--------------------+------+-----+------+------+----+ | | Diagnostic Log | - | +--------------------+------+---------------------+ | + | +--------------------+------+---------------------+ | | | 2026-06-12 22:00 | info | Cache rebuilt | | - | +--------------------+------+---------------------+ | + | +--------------------+------+---------------------+ | +---------------------------------------------------------+ """ if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_diagnostics_panel") @@ -1777,20 +1771,15 @@ def render_diagnostics_panel(app: App) -> None: ("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() + 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() app.perf_show_graphs.setdefault(key, False) _, app.perf_show_graphs[key] = imgui.checkbox(f"##g_{key}", app.perf_show_graphs[key]) imgui.end_table() - + if app.perf_profiling_enabled: imgui.separator() imgui.text("Detailed Component Timings (Moving Average)") @@ -1805,29 +1794,22 @@ def render_diagnostics_panel(app: App) -> None: 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(theme.get_color("status_error"), 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}") + 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(theme.get_color("status_error"), 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() app.perf_show_graphs.setdefault(comp_name, False) _, app.perf_show_graphs[comp_name] = imgui.checkbox(f"##g_{comp_name}", app.perf_show_graphs[comp_name]) imgui.end_table() - + imgui.separator() imgui.text("Performance Graphs") for key, show in app.perf_show_graphs.items(): @@ -1843,35 +1825,31 @@ def render_diagnostics_panel(app: App) -> None: 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("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(app.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(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 app.perf_profiling_enabled: app.perf_monitor.end_component("_render_diagnostics_panel") def render_cache_panel(app: App) -> None: - """ - Renders Gemini cache analytics. Shows cache age, TTL remaining as a colour-coded + """Renders Gemini cache analytics. Shows cache age, TTL remaining as a colour-coded progress bar (green > 50%, yellow > 20%, red otherwise), and a [Clear Cache] button. Skips rendering for non-Gemini providers. - SSDL Shape: - `[I:cache_stats] -> [B:progress_bar] => [B:clear_cache]` + SSDL: `[I:cache_stats] -> [B:progress_bar] => [B:clear_cache]` ASCII Layout Map: +---------------------------------------------------------+ | Cache Analytics | - | Age: 4m 12s TTL: 55m 48s (93%) | + | Age: 4m 12s TTL: 55m 48s (93%) | | [============================================= ] 93% | | [Clear Cache] | | Cache cleared - will rebuild on next request | @@ -1892,7 +1870,7 @@ def render_cache_panel(app: App) -> None: ttl_total = stats.get("ttl_seconds", 3600) age_str = f"{age_sec/60:.0f}m {age_sec%60:.0f}s" remaining_str = f"{ttl_remaining/60:.0f}m {ttl_remaining%60:.0f}s" - ttl_pct = (ttl_remaining / ttl_total * 100) if ttl_total > 0 else 0 + ttl_pct = (ttl_remaining / ttl_total * 100) if ttl_total > 0 else 0 imgui.text(f"Age: {age_str}") imgui.text(f"TTL: {remaining_str} ({ttl_pct:.0f}%)") color = theme.get_color("status_success") @@ -1909,12 +1887,10 @@ def render_cache_panel(app: App) -> None: if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_cache_panel") def render_tool_analytics_panel(app: App) -> None: - """ - Renders a breakdown of tool execution telemetry: calls, average latency ms, and + """Renders a breakdown of tool execution telemetry: calls, average latency ms, and failure rate percentage per tool. Results are sorted by invocation count descending. - SSDL Shape: - `[I:tool_stats] -> [B:stats_table]` + SSDL: `[I:tool_stats] -> [B:stats_table]` ASCII Layout Map: +---------------------------------------------------------+ @@ -1952,12 +1928,9 @@ def render_tool_analytics_panel(app: App) -> None: avg_time = total_time / count if count > 0 else 0 fail_pct = (failures / count * 100) if count > 0 else 0 imgui.table_next_row() - imgui.table_set_column_index(0) - imgui.text(tool_name) - imgui.table_set_column_index(1) - imgui.text(str(count)) - imgui.table_set_column_index(2) - imgui.text(f"{avg_time:.0f}") + imgui.table_set_column_index(0); imgui.text(tool_name) + imgui.table_set_column_index(1); imgui.text(str(count)) + imgui.table_set_column_index(2); imgui.text(f"{avg_time:.0f}") imgui.table_set_column_index(3) if fail_pct > 0: imgui.text_colored(theme.get_color("status_error"), f"{fail_pct:.0f}%") else: imgui.text("0%") @@ -1965,28 +1938,26 @@ def render_tool_analytics_panel(app: App) -> None: if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_tool_analytics_panel") def render_token_budget_panel(app: App) -> None: - """ - Renders prompt token utilization breakdown: session totals, cache read/creation stats, + """Renders prompt token utilization breakdown: session totals, cache read/creation stats, a colour-coded utilisation progress bar, component breakdown table (System/Tools/History), per-tier MMA cost table, trim warnings, and cache activity badge. - SSDL Shape: - `[I:session_tokens] -> [B:utilization_bar] -> [B:breakdown_table] -> [B:tier_costs_table]` + SSDL: `[I:session_tokens] -> [B:utilization_bar] -> [B:breakdown_table] -> [B:tier_costs_table]` ASCII Layout Map: +---------------------------------------------------------+ | Prompt Utilization | - | Tokens: 24,200 (In: 18,000 Out: 6,200) Latency: 2.1s | + | Tokens: 24,200 (In: 18,000 Out: 6,200) Latency: 2.1s | | Cache Read: 12,000 Creation: 2,000 | | [============================== ] 63.2% | | 24,200 / 38,000 tokens (13,800 remaining) | - | +-------------+--------+------+ | - | | System | 8,000 | 33% | | - | | Tools | 4,000 | 17% | | - | | History | 12,200 | 50% | | - | +-------------+--------+------+ | + | +-------------+--------+------+ | + | | System | 8,000 | 33% | | + | | Tools | 4,000 | 17% | | + | | History | 12,200 | 50% | | + | +-------------+--------+------+ | | MMA Tier Costs | - | | Tier 1 | gemini | 14,000 | $0.0012 | | + | | Tier 1 | gemini | 14,000 | $0.0012 | | | ⚠ WARNING: Next call will trim history | | Cache Usage: ACTIVE | Age: 252s / 3600s | +---------------------------------------------------------+ @@ -1999,7 +1970,7 @@ def render_token_budget_panel(app: App) -> None: 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) + if app._gemini_cache_text: imgui.text_colored(C_SUB(), app._gemini_cache_text) imgui.separator() if app._token_stats_dirty: @@ -2107,12 +2078,10 @@ def render_token_budget_panel(app: App) -> None: #region: Logging def render_log_management(app: App) -> None: - """ - Renders the Log Management window. Enables browsing, starring (whitelisting), and loading + """Renders the Log Management window. Enables browsing, starring (whitelisting), and loading of prior log sessions from the log registry. Provides one-click prune and refresh actions. - SSDL Shape: - `[B:refresh | load | prune] -> [I:sessions_table] => [B:load | star/unstar per row]` + SSDL: `[B:refresh | load | prune] -> [I:sessions_table] => [B:load | star/unstar per row]` ASCII Layout Map: +---------------------------------------------------------+ @@ -2122,9 +2091,9 @@ def render_log_management(app: App) -> None: | | Session ID | Start Time | Star| Size KB| Msgs | | | +------------+------------------+-----+--------+------+ | | | 20260612_2 | 2026-06-12 22:00 | YES | 120 | 84 | | - | | | | | [Load] [Unstar] | + | | | | | [Load] [Unstar] | | | 20260611_1 | 2026-06-11 18:30 | NO | 340 | 210 | | - | | | | | [Load] [Star] | + | | | | | [Load] [Star] | | +------------+------------------+-----+--------+------+ | +---------------------------------------------------------+ """ @@ -2159,45 +2128,35 @@ def render_log_management(app: App) -> None: 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() + 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(theme.get_color("status_warning"), "YES") - else: - imgui.text("NO") + if whitelisted: imgui.text_colored(theme.get_color("status_warning"), "YES") + else: imgui.text("NO") + imgui.table_next_column() 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}"): - app.cb_load_prior_log(s_data.get("path")) + 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}"): app.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 "") + 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" + 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() @@ -2208,11 +2167,9 @@ def render_log_management(app: App) -> None: #region: Project Management def render_project_settings_hub(app: App) -> None: - """ - Renders the Project Settings Hub: a two-tab container for Projects and Paths configuration. + """Renders the Project Settings Hub: a two-tab container for Projects and Paths configuration. - SSDL Shape: - `[I:tab_bar] => [I:projects_panel] | [I:paths_panel]` + SSDL: `[I:tab_bar] => [I:projects_panel] | [I:paths_panel]` ASCII Layout Map: +---------------------------------------------------------+ @@ -2229,13 +2186,11 @@ def render_project_settings_hub(app: App) -> None: if exp: render_paths_panel(app) def render_projects_panel(app: App) -> None: - """ - Renders the project configuration panel. Allows setting execution mode, + """Renders the project configuration panel. Allows setting execution mode, repository path, output/conductor directories, managing projects list, and global layout toggles (word-wrap, auto-scroll). - SSDL Shape: - `[I:active_project] -> [B:execution_mode] -> [B:directories] -> [I:project_files] -> [B:project_actions] -> [B:toggles]` + SSDL: `[I:active_project] -> [B:execution_mode] -> [B:directories] -> [I:project_files] -> [B:project_actions] -> [B:toggles]` ASCII Layout Map: +---------------------------------------------------------+ @@ -2258,7 +2213,7 @@ def render_projects_panel(app: App) -> None: imgui.text_colored(C_IN(), f"Active: {proj_name}") imgui.separator() imgui.text("Execution Mode") - modes = ["native", "beads"] + modes = ["native", "beads"] current_idx = modes.index(app.ui_project_execution_mode) if app.ui_project_execution_mode in modes else 0 ch, new_idx = imgui.combo("##exec_mode", current_idx, modes) if ch: app.ui_project_execution_mode = modes[new_idx] @@ -2300,21 +2255,20 @@ 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() + if is_active: imgui.pop_style_color() imgui.same_line() imgui.text_colored(C_LBL(), pp) imgui.end_child() if imgui.button("Add Project"): r = hide_tk_root() p = filedialog.askopenfilename( - title="Select Project .toml", - filetypes=[("TOML", "*.toml"), ("All", "*.*")], + title = "Select Project .toml", + filetypes = [("TOML", "*.toml"), ("All", "*.*")], ) r.destroy() - if p and p not in app.project_paths: - app.project_paths.append(p) + if p and p not in app.project_paths: app.project_paths.append(p) imgui.same_line() if imgui.button("New Project"): r = hide_tk_root() @@ -2338,12 +2292,10 @@ def render_projects_panel(app: App) -> None: if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_projects_panel") def render_paths_panel(app: App) -> None: - """ - Renders the System Path Configuration panel. Shows source-tagged logs and scripts + """Renders the System Path Configuration panel. Shows source-tagged logs and scripts directory fields, each with a Browse button, and Apply / Reset buttons. - SSDL Shape: - `[I:path_fields] => [B:apply_reset]` + SSDL: `[I:path_fields] => [B:apply_reset]` ASCII Layout Map: +---------------------------------------------------------+ @@ -2367,11 +2319,11 @@ def render_paths_panel(app: App) -> None: if imgui.is_item_hovered(): imgui.set_tooltip(tooltip) imgui.same_line() imgui.text_disabled(f"(Source: {info['source']})") - - val = getattr(app, attr) + + val = getattr(app, attr) changed, new_val = imgui.input_text(f"##{key}", val) if imgui.is_item_hovered(): imgui.set_tooltip(tooltip) - if changed: setattr(app, attr, new_val) + if changed: setattr(app, attr, new_val) imgui.same_line() if imgui.button(f"Browse##{key}"): r = hide_tk_root() @@ -2399,7 +2351,7 @@ def render_ai_settings_hub(app: App) -> None: """Groups and renders all AI-related configuration panels in a unified hub sidebar. Includes persona selection, LLM provider settings, system prompts, RAG config, and tools. - SSDL Shape: `[I:persona_selector] -> [B:provider_header] -> [B:system_prompts_header] -> [B:rag_header] -> [I:agent_tools]` + SSDL: `[I:persona_selector] -> [B:provider_header] -> [B:system_prompts_header] -> [B:rag_header] -> [I:agent_tools]` ASCII Layout Map: +---------------------------------------------------------+ @@ -2420,12 +2372,10 @@ def render_ai_settings_hub(app: App) -> None: render_agent_tools_panel(app) def render_rag_panel(app: App) -> None: - """ - Renders RAG configuration panel. Exposes enable toggle, vector-store and embedding + """Renders RAG configuration panel. Exposes enable toggle, vector-store and embedding provider selectors, chunk size/overlap inputs, RAG status label, and a Rebuild Index button. - SSDL Shape: - `[B:rag_switch] -> [B:combo_selectors] -> [B:chunking_inputs] => [B:rebuild_index]` + SSDL: `[B:rag_switch] -> [B:combo_selectors] -> [B:chunking_inputs] => [B:rebuild_index]` ASCII Layout Map: +---------------------------------------------------------+ @@ -2444,23 +2394,23 @@ def render_rag_panel(app: App) -> None: imgui.text("Vector Store Provider") providers = ['chroma', 'qdrant', 'mock'] + #NOTE(Ed): Exception(Thirdparty) try: idx = providers.index(conf.vector_store.provider) except (ValueError, AttributeError): idx = 0 ch2, next_idx = imgui.combo("##rag_provider", idx, providers) - if ch2: - conf.vector_store.provider = providers[next_idx] + if ch2: conf.vector_store.provider = providers[next_idx] imgui.text("Embedding Provider") emb_providers = ['gemini', 'local'] + #NOTE(Ed): Exception(Thirdparty) try: idx_e = emb_providers.index(conf.embedding_provider) except (ValueError, AttributeError): idx_e = 0 ch3, next_idx_e = imgui.combo("##rag_emb_provider", idx_e, emb_providers) - if ch3: - conf.embedding_provider = emb_providers[next_idx_e] + if ch3: conf.embedding_provider = emb_providers[next_idx_e] imgui.text("Chunk Size") imgui.set_next_item_width(150) @@ -2475,13 +2425,11 @@ def render_rag_panel(app: App) -> None: if imgui.button("Rebuild Index"): app.controller.event_queue.put('click', 'btn_rebuild_rag_index') def render_system_prompts_panel(app: App) -> None: - """ - Renders the System Prompts panel. Exposes global preset selector + multiline edit, + """Renders the System Prompts panel. Exposes global preset selector + multiline edit, base prompt toggle (default vs custom) with diff + reset, and project-level preset selector + multiline edit. - SSDL Shape: - `[B:global_preset_combo] -> [I:global_text] -> [B:base_prompt_header] -> [I:base_text] -> [B:project_preset_combo] -> [I:project_text]` + SSDL: `[B:global_preset_combo] -> [I:global_text] -> [B:base_prompt_header] -> [I:base_text] -> [B:project_preset_combo] -> [I:project_text]` ASCII Layout Map: +---------------------------------------------------------+ @@ -2492,7 +2440,7 @@ def render_system_prompts_panel(app: App) -> None: | +-----------------------------------------------------+ | | [x] Use Default Base System Prompt | | [Reset to Default] [Show Diff] (?) | - | > Base System Prompt (foundational instructions) | + | > Base System Prompt (foundational instructions) | | [read-only or editable text area] | | Project System Prompt | | [feature-mode v] [Manage Presets] | @@ -2545,7 +2493,7 @@ def render_system_prompts_panel(app: App) -> None: for name in preset_names: is_sel = (name == current_project) if imgui.selectable(name, is_sel)[0]: app.controller._apply_preset(name, "project") - if is_sel: imgui.set_item_default_focus() + if is_sel: imgui.set_item_default_focus() imgui.end_combo() imgui.same_line(0, 8) if imgui.button("Manage Presets##project"): app.show_preset_manager_window = True @@ -2553,13 +2501,11 @@ def render_system_prompts_panel(app: App) -> None: ch, app.ui_project_system_prompt = imgui.input_text_multiline("##psp", app.ui_project_system_prompt, imgui.ImVec2(-1, 100)) def render_agent_tools_panel(app: App) -> None: - """ - Renders the Active Tool Presets & Biases collapsible section. Shows a preset combo, + """Renders the Active Tool Presets & Biases collapsible section. Shows a preset combo, a Manage Presets button, and a Bias Profile combo. Displays a disabled notice when tool calling is unsupported by the active provider/model. - SSDL Shape: - `[B:collapsing_header] => [B:preset_combo] -> [B:manage_presets] -> [B:bias_combo]` + SSDL: `[B:collapsing_header] => [B:preset_combo] -> [B:manage_presets] -> [B:bias_combo]` ASCII Layout Map: +---------------------------------------------------------+ @@ -2580,14 +2526,14 @@ def render_agent_tools_panel(app: App) -> None: active = getattr(app, "ui_active_tool_preset", "") if active is None: active = "" + #NOTE(Ed): Exception(Thirdparty) try: idx = preset_names.index(active) except ValueError: idx = 0 ch, new_idx = imgui.combo("##tool_preset_select", idx, preset_names) - if ch: - app.ui_active_tool_preset = preset_names[new_idx] + if ch: app.ui_active_tool_preset = preset_names[new_idx] imgui.same_line() if imgui.button("Manage Presets##tools"): app.show_tool_preset_manager_window = True @@ -2608,14 +2554,16 @@ def render_agent_tools_panel(app: App) -> None: imgui.dummy(imgui.ImVec2(0, 8)) cat_options = ["All"] + sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys())) + #NOTE(Ed): Exception(Thirdparty) try: f_idx = cat_options.index(app.ui_tool_filter_category) except ValueError: f_idx = 0 + imgui.set_next_item_width(200) ch_cat, next_f_idx = imgui.combo("Filter Category##agent", f_idx, cat_options) if ch_cat: app.ui_tool_filter_category = cat_options[next_f_idx] - + imgui.dummy(imgui.ImVec2(0, 8)) active_name = app.ui_active_tool_preset if active_name and active_name in presets: @@ -2624,10 +2572,10 @@ def render_agent_tools_panel(app: App) -> None: if app.ui_tool_filter_category != "All" and app.ui_tool_filter_category != cat_name: continue if imgui.tree_node(cat_name): for tool in tools: - if tool.weight >= 5: imgui.text_colored(theme.get_color("status_error"), "[HIGH]"); imgui.same_line() - elif tool.weight == 4: imgui.text_colored(theme.get_color("status_warning"), "[PREF]"); imgui.same_line() + if tool.weight >= 5: imgui.text_colored(theme.get_color("status_error"), "[HIGH]"); imgui.same_line() + elif tool.weight == 4: imgui.text_colored(theme.get_color("status_warning"), "[PREF]"); imgui.same_line() elif tool.weight == 2: imgui.text_colored(theme.get_color("status_warning"), "[REJECT]"); imgui.same_line() - elif tool.weight <= 1: imgui.text_colored(theme.get_color("text_disabled"), "[LOW]"); imgui.same_line() + elif tool.weight <= 1: imgui.text_colored(theme.get_color("text_disabled"), "[LOW]"); imgui.same_line() imgui.text(tool.name); imgui.same_line(180) @@ -2638,17 +2586,10 @@ def render_agent_tools_panel(app: App) -> None: imgui.tree_pop() def render_provider_panel(app: App) -> None: - """ - Renders the LLM provider configuration panel. Allows selection of API providers, + """Renders the LLM provider configuration panel. Allows selection of API providers, active models, hyper-parameters (temperature, max tokens, Top-P), history limits, and Gemini CLI binaries. - - State Mutations: - app.current_provider, app.current_model - app.temperature, app.max_tokens, app.top_p, app.history_trunc_limit - app.ui_gemini_cli_path - - SSDL Shape: - `[I:providers] -> [B:provider_combo] -> [B:model_listbox] -> [B:parameters_sliders] => [B:cli_path_browse]` + + SSDL: `[I:providers] -> [B:provider_combo] -> [B:model_listbox] -> [B:parameters_sliders] => [B:cli_path_browse]` """ if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_provider_panel") imgui.text("Provider") @@ -2666,7 +2607,9 @@ def render_provider_panel(app: App) -> None: if app.current_provider == "llama": base_url = getattr(ai_client, "_llama_base_url", "") imgui.set_tooltip(f"Local backend: {base_url or 'unknown'}" if base_url else "Local backend") + _render_v2_capability_badges(caps) + imgui.separator() imgui.text("Model") if imgui.begin_list_box("##models", imgui.ImVec2(-1, 120)): @@ -2728,21 +2671,10 @@ def render_provider_panel(app: App) -> None: if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_provider_panel") def render_persona_selector_panel(app: App) -> None: - """ - Renders the Persona Selection panel. Allows selecting profiles, editing persona definitions, + """Renders the Persona Selection panel. Allows selecting profiles, editing persona definitions, overriding system parameters (models, presets, prompts, bias profiles) and loading preset contexts. - State Mutations: - app.ui_active_persona, app._editing_persona_name, app._editing_persona_system_prompt, - app._editing_persona_tool_preset_id, app._editing_persona_bias_profile_id, - app._editing_persona_context_preset_id, app._editing_persona_aggregation_strategy, - app._editing_persona_preferred_models_list, app._editing_persona_is_new, - app.current_provider, app.current_model, app.temperature, app.max_tokens, - app.history_trunc_limit, app.ui_project_system_prompt, app.ui_active_tool_preset, - app.ui_active_bias_profile, app.ui_active_context_preset, app.show_persona_editor_window - - SSDL Shape: - `[I:personas] -> [B:persona_combo] => [B:manage_personas]` + SSDL: `[I:personas] -> [B:persona_combo] => [B:manage_personas]` """ if app.perf_profiling_enabled: app.perf_monitor.start_component("_render_persona_selector_panel") imgui.text("Persona") @@ -2756,28 +2688,26 @@ def render_persona_selector_panel(app: App) -> None: app.ui_active_persona = pname if pname in personas: persona = personas[pname] - app._editing_persona_name = persona.name - app._editing_persona_system_prompt = persona.system_prompt or "" - app._editing_persona_tool_preset_id = persona.tool_preset or "" - app._editing_persona_bias_profile_id = persona.bias_profile or "" - app._editing_persona_context_preset_id = getattr(persona, 'context_preset', '') or "" - app._editing_persona_aggregation_strategy = getattr(persona, 'aggregation_strategy', '') or "" + app._editing_persona_name = persona.name + app._editing_persona_system_prompt = persona.system_prompt or "" + app._editing_persona_tool_preset_id = persona.tool_preset or "" + app._editing_persona_bias_profile_id = persona.bias_profile or "" + app._editing_persona_context_preset_id = getattr(persona, 'context_preset', '') or "" + app._editing_persona_aggregation_strategy = getattr(persona, 'aggregation_strategy', '') or "" app._editing_persona_preferred_models_list = copy.deepcopy(persona.preferred_models) if persona.preferred_models else [] - app._editing_persona_is_new = False + app._editing_persona_is_new = False # Apply persona to current state immediately if persona.preferred_models and len(persona.preferred_models) > 0: first_model = persona.preferred_models[0] - if first_model.get("provider"): - app.current_provider = first_model.get("provider") - if first_model.get("model"): - app.current_model = first_model.get("model") + if first_model.get("provider"): app.current_provider = first_model.get("provider") + if first_model.get("model"): app.current_model = first_model.get("model") if first_model.get("temperature") is not None: ai_client.temperature = first_model.get("temperature") - app.temperature = first_model.get("temperature") + app.temperature = first_model.get("temperature") if first_model.get("max_output_tokens"): ai_client.max_output_tokens = first_model.get("max_output_tokens") - app.max_tokens = first_model.get("max_output_tokens") + app.max_tokens = first_model.get("max_output_tokens") if first_model.get("history_trunc_limit"): app.history_trunc_limit = first_model.get("history_trunc_limit") @@ -2790,6 +2720,7 @@ def render_persona_selector_panel(app: App) -> None: ai_client.set_bias_profile(persona.bias_profile) if getattr(persona, 'context_preset', None): app.ui_active_context_preset = persona.context_preset + #TODO(Ed): Exception(Review) try: app.load_context_preset(persona.context_preset) except KeyError as e: @@ -2801,44 +2732,39 @@ def render_persona_selector_panel(app: App) -> None: app.show_persona_editor_window = True if app.ui_active_persona and app.ui_active_persona in personas: persona = personas[app.ui_active_persona] - app._editing_persona_name = persona.name - app._editing_persona_system_prompt = persona.system_prompt or "" - app._editing_persona_tool_preset_id = persona.tool_preset or "" - app._editing_persona_bias_profile_id = persona.bias_profile or "" - app._editing_persona_context_preset_id = getattr(persona, 'context_preset', '') or "" - app._editing_persona_aggregation_strategy = getattr(persona, 'aggregation_strategy', '') or "" + app._editing_persona_name = persona.name + app._editing_persona_system_prompt = persona.system_prompt or "" + app._editing_persona_tool_preset_id = persona.tool_preset or "" + app._editing_persona_bias_profile_id = persona.bias_profile or "" + app._editing_persona_context_preset_id = getattr(persona, 'context_preset', '') or "" + app._editing_persona_aggregation_strategy = getattr(persona, 'aggregation_strategy', '') or "" app._editing_persona_preferred_models_list = copy.deepcopy(persona.preferred_models) if persona.preferred_models else [] - app._editing_persona_scope = app.controller.persona_manager.get_persona_scope(persona.name) - app._editing_persona_is_new = False + app._editing_persona_scope = app.controller.persona_manager.get_persona_scope(persona.name) + app._editing_persona_is_new = False else: - app._editing_persona_name = "" - app._editing_persona_system_prompt = "" - app._editing_persona_tool_preset_id = "" - app._editing_persona_bias_profile_id = "" - app._editing_persona_context_preset_id = "" - app._editing_persona_aggregation_strategy = "" + app._editing_persona_name = "" + app._editing_persona_system_prompt = "" + app._editing_persona_tool_preset_id = "" + app._editing_persona_bias_profile_id = "" + app._editing_persona_context_preset_id = "" + app._editing_persona_aggregation_strategy = "" app._editing_persona_preferred_models_list = [{ - "provider": app.current_provider, - "model": app.current_model, - "temperature": getattr(app, "temperature", 0.7), - "max_output_tokens": getattr(app, "max_tokens", 4096), + "provider": app.current_provider, + "model": app.current_model, + "temperature": getattr(app, "temperature", 0.7), + "max_output_tokens": getattr(app, "max_tokens", 4096), "history_trunc_limit": getattr(app, "history_trunc_limit", 900000) }] - app._editing_persona_scope = "project" + app._editing_persona_scope = "project" app._editing_persona_is_new = True imgui.separator() if app.perf_profiling_enabled: app.perf_monitor.end_component("_render_persona_selector_panel") def render_base_prompt_diff_modal(app: App) -> None: - """ - Renders a popup modal showing a unified diff between the default system prompt + """Renders a popup modal showing a unified diff between the default system prompt and the current custom base prompt. - State Mutations: - app.controller._show_base_prompt_diff_modal (closes/hides modal) - - SSDL Shape: - `[I:unified_diff] -> [I:diff_view_box] => [B:close]` + SSDL: `[I:unified_diff] -> [I:diff_view_box] => [B:close]` """ if not getattr(app.controller, "_show_base_prompt_diff_modal", False): return @@ -2869,16 +2795,9 @@ def render_base_prompt_diff_modal(app: App) -> None: imgui.end_popup() def render_save_preset_modal(app: App) -> None: - """ - Renders the popup modal for saving the current ImGui window layout preset. + """Renders the popup modal for saving the current ImGui window layout preset. - State Mutations: - app._show_save_preset_modal (toggles visibility) - app._new_preset_name (stores input layout name) - app.layout_presets (saves layout definitions to configuration) - - SSDL Shape: - `[I:preset_inputs] => [B:save_cancel]` + SSDL: `[I:preset_inputs] => [B:save_cancel]` """ if not app._show_save_preset_modal: return imgui.open_popup("Save Layout Preset") @@ -2890,13 +2809,13 @@ def render_save_preset_modal(app: App) -> None: if app._new_preset_name.strip(): ini_data = imgui.save_ini_settings_to_memory() app.layout_presets[app._new_preset_name.strip()] = { - "ini": ini_data, + "ini": ini_data, "multi_viewport": app.ui_multi_viewport } app.config["layout_presets"] = app.layout_presets app.save_config() app._show_save_preset_modal = False - app._new_preset_name = "" + app._new_preset_name = "" imgui.close_current_popup() imgui.same_line() if imgui.button("Cancel", imgui.ImVec2(120, 0)): @@ -2904,22 +2823,16 @@ def render_save_preset_modal(app: App) -> None: imgui.close_current_popup() def render_preset_manager_content(app: App, is_embedded: bool = False) -> None: - """ - Renders the core prompt preset manager interface including the sidebar preset selector + """Renders the core prompt preset manager interface including the sidebar preset selector and the preset prompt content text editor. - State Mutations: - app._selected_preset_idx - app._editing_preset_name, app._editing_preset_system_prompt, app._editing_preset_scope - - SSDL Shape: - `[I:presets_list] -> [B:new_preset] -> [I:editor_meta] -> [I:editor_textbox] => [B:save_delete]` + SSDL: `[I:presets_list] -> [B:new_preset] -> [I:editor_meta] -> [I:editor_textbox] => [B:save_delete]` """ avail = imgui.get_content_region_avail() if not hasattr(app, "_prompt_md_preview"): app._prompt_md_preview = False if imgui.begin_table("prompt_main_split", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v): - imgui.table_setup_column("List", imgui.TableColumnFlags_.width_fixed, 200) + imgui.table_setup_column("List", imgui.TableColumnFlags_.width_fixed, 200) imgui.table_setup_column("Editor", imgui.TableColumnFlags_.width_stretch) imgui.table_next_row() @@ -2928,19 +2841,19 @@ def render_preset_manager_content(app: App, is_embedded: bool = False) -> None: imgui.begin_child("prompt_list_pane", imgui.ImVec2(0, 0), False) if True: if imgui.button("New Preset", imgui.ImVec2(-1, 0)): - app._editing_preset_name = "" + app._editing_preset_name = "" app._editing_preset_system_prompt = "" - app._editing_preset_scope = "project" - app._selected_preset_idx = -1 + app._editing_preset_scope = "project" + app._selected_preset_idx = -1 imgui.separator() preset_names = sorted(app.controller.presets.keys()) for i, name in enumerate(preset_names): if name and imgui.selectable(f"{name}##p_{i}", app._selected_preset_idx == i)[0]: - app._selected_preset_idx = i - app._editing_preset_name = name - p = app.controller.presets[name] + app._selected_preset_idx = i + app._editing_preset_name = name + p = app.controller.presets[name] app._editing_preset_system_prompt = p.system_prompt - app._editing_preset_scope = app.controller.preset_manager.get_preset_scope(name) + app._editing_preset_scope = app.controller.preset_manager.get_preset_scope(name) imgui.end_child() # Right Editor @@ -2966,21 +2879,21 @@ def render_preset_manager_content(app: App, is_embedded: bool = False) -> None: imgui.same_line() if imgui.radio_button("Project##ps", app._editing_preset_scope == "project"): app._editing_preset_scope = "project" imgui.end_table() - + imgui.dummy(imgui.ImVec2(0, 4)) imgui.separator() imgui.text("Prompt Content:") imgui.same_line() if imgui.button("Pop out MD Preview"): - app.text_viewer_title = f"Preset: {app._editing_preset_name}" - app.text_viewer_content = app._editing_preset_system_prompt - app.text_viewer_type = "markdown" + app.text_viewer_title = f"Preset: {app._editing_preset_name}" + app.text_viewer_content = app._editing_preset_system_prompt + app.text_viewer_type = "markdown" app.show_windows["Text Viewer"] = True rem_y = imgui.get_content_region_avail().y _, app._editing_preset_system_prompt = imgui.input_text_multiline("##pcont", app._editing_preset_system_prompt, imgui.ImVec2(-1, rem_y)) imgui.end_child() - + # Footer Buttons imgui.separator() imgui.dummy(imgui.ImVec2(0, 4)) @@ -3004,14 +2917,9 @@ def render_preset_manager_content(app: App, is_embedded: bool = False) -> None: imgui.end_table() def render_preset_manager_window(app: App, is_embedded: bool = False) -> None: - """ - Renders the window container for the Prompt Presets Manager. + """Renders the window container for the Prompt Presets Manager. - State Mutations: - app.show_preset_manager_window (visibility toggle) - - SSDL Shape: - `[I] -> [I:window] => [I:preset_manager_content]` + SSDL: `[I] -> [I:window] => [I:preset_manager_content]` """ if not app.show_preset_manager_window and not is_embedded: return if not is_embedded: @@ -3023,15 +2931,9 @@ def render_preset_manager_window(app: App, is_embedded: bool = False) -> None: render_preset_manager_content(app, is_embedded=is_embedded) def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> None: - """ - Renders the tool presets and tool capability profiles editor layout. + """Renders the tool presets and tool capability profiles editor layout. - State Mutations: - app._editing_tool_preset_name, app._editing_tool_preset_scope, app._selected_tool_preset_idx - app.controller (saves/deletes tool presets) - - SSDL Shape: - `[I:tool_presets] -> [B:new_tool_preset] -> [I:capability_toggles] -> [I:tools_list] => [B:save_delete]` + SSDL: `[I:tool_presets] -> [B:new_tool_preset] -> [I:capability_toggles] -> [I:tools_list] => [B:save_delete]` """ avail = imgui.get_content_region_avail() if not hasattr(app, "_tool_split_v"): app._tool_split_v = 0.4 @@ -3071,8 +2973,8 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N 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) - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Name:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, app._editing_tool_preset_name = imgui.input_text("##etpn", app._editing_tool_preset_name) + imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 80); imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Name:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, app._editing_tool_preset_name = imgui.input_text("##etpn", app._editing_tool_preset_name) imgui.table_next_row(); imgui.table_next_column(); imgui.text("Scope:"); imgui.table_next_column() if imgui.radio_button("Global", app._editing_tool_preset_scope == "global"): app._editing_tool_preset_scope = "global" imgui.same_line(); @@ -3081,9 +2983,9 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N rem_y = imgui.get_content_region_avail().y - 80 if app._tool_list_open and app._bias_list_open: h1, h2 = rem_y * app._tool_split_v, rem_y - (rem_y * app._tool_split_v) - 10 - elif app._tool_list_open: h1, h2 = rem_y, 0 - elif app._bias_list_open: h1, h2 = 0, rem_y - else: h1, h2 = 0, 0 + elif app._tool_list_open: h1, h2 = rem_y, 0 + elif app._bias_list_open: h1, h2 = 0, rem_y + else: h1, h2 = 0, 0 imgui.dummy(imgui.ImVec2(0, 4)) opened_t = imgui.collapsing_header("Categories & Tools", imgui.TreeNodeFlags_.default_open) @@ -3091,7 +2993,7 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N if app._tool_list_open: imgui.text("Filter:"); imgui.same_line() cat_opts = ["All"] + sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys())) - f_idx = cat_opts.index(app.ui_tool_filter_category) if app.ui_tool_filter_category in cat_opts else 0 + f_idx = cat_opts.index(app.ui_tool_filter_category) if app.ui_tool_filter_category in cat_opts else 0 imgui.set_next_item_width(200); ch_cat, next_f_idx = imgui.combo("##tp_filter", f_idx, cat_opts) if ch_cat: app.ui_tool_filter_category = cat_opts[next_f_idx] with imscope.child("tp_scroll", 0, h1, True): @@ -3121,7 +3023,7 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N if app._bias_list_open: imgui.button("###tool_splitter", imgui.ImVec2(-1, 4)) if imgui.is_item_active(): app._tool_split_v = max(0.1, min(0.9, app._tool_split_v + imgui.get_io().mouse_delta.y / rem_y)) - + imgui.dummy(imgui.ImVec2(0, 4)) opened_b = imgui.collapsing_header("Bias Profiles", imgui.TreeNodeFlags_.default_open) if opened_b != app._bias_list_open: app._bias_list_open = opened_b @@ -3134,24 +3036,30 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N imgui.begin_child("blist_pane", imgui.ImVec2(0, 0), False) if True: if imgui.button("New Profile", imgui.ImVec2(-1, 0)): - app._editing_bias_profile_name = ""; app._editing_bias_profile_tool_weights = {} - app._editing_bias_profile_category_multipliers = {}; app._selected_bias_profile_idx = -1 + app._editing_bias_profile_name = "" + app._editing_bias_profile_tool_weights = {} + app._editing_bias_profile_category_multipliers = {} + app._selected_bias_profile_idx = -1 imgui.separator(); bnames = sorted(app.bias_profiles.keys()) for i, bname in enumerate(bnames): if bname and imgui.selectable(f"{bname}##b_{i}", app._selected_bias_profile_idx == i)[0]: - app._selected_bias_profile_idx = i; app._editing_bias_profile_name = bname; prof = app.bias_profiles[bname] - app._editing_bias_profile_tool_weights = copy.deepcopy(prof.tool_weights); app._editing_bias_profile_category_multipliers = copy.deepcopy(prof.category_multipliers) + app._selected_bias_profile_idx = i + app._editing_bias_profile_name = bname; + prof = app.bias_profiles[bname] + app._editing_bias_profile_tool_weights = copy.deepcopy(prof.tool_weights) + app._editing_bias_profile_category_multipliers = copy.deepcopy(prof.category_multipliers) imgui.end_child() imgui.table_next_column() imgui.begin_child("bedit_pane", imgui.ImVec2(0, 0), False) if True: - imgui.text("Name:"); imgui.same_line(); imgui.set_next_item_width(-1); _, app._editing_bias_profile_name = imgui.input_text("##bname", app._editing_bias_profile_name) - rem_bias_y = imgui.get_content_region_avail().y - 45 + imgui.text("Name:"); imgui.same_line(); imgui.set_next_item_width(-1) + _, app._editing_bias_profile_name = imgui.input_text("##bname", app._editing_bias_profile_name) + rem_bias_y = imgui.get_content_region_avail().y - 45 if app._bias_weights_open and app._bias_cats_open: bh1, bh2 = rem_bias_y * app._bias_split_v, rem_bias_y - (rem_bias_y * app._bias_split_v) - 10 - elif app._bias_weights_open: bh1, bh2 = rem_bias_y, 0 - elif app._bias_cats_open: bh1, bh2 = 0, rem_bias_y - else: bh1, bh2 = 0, 0 + elif app._bias_weights_open: bh1, bh2 = rem_bias_y, 0 + elif app._bias_cats_open: bh1, bh2 = 0, rem_bias_y + else: bh1, bh2 = 0, 0 opened_bw = imgui.collapsing_header("Tool Weights", imgui.TreeNodeFlags_.default_open) if opened_bw != app._bias_weights_open: app._bias_weights_open = opened_bw @@ -3161,10 +3069,12 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N for cat_name, default_tools in models.DEFAULT_TOOL_CATEGORIES.items(): if imgui.tree_node(f"{cat_name}##b_list"): if imgui.begin_table(f"bt_{cat_name}", 2): - imgui.table_setup_column("T", imgui.TableColumnFlags_.width_fixed, 220); imgui.table_setup_column("W", imgui.TableColumnFlags_.width_stretch) + imgui.table_setup_column("T", imgui.TableColumnFlags_.width_fixed, 220) + imgui.table_setup_column("W", imgui.TableColumnFlags_.width_stretch) for tn in default_tools: - imgui.table_next_row(); imgui.table_next_column(); imgui.text(tn); imgui.table_next_column() - curr_w = app._editing_bias_profile_tool_weights.get(tn, 3); imgui.set_next_item_width(-1) + imgui.table_next_row(); imgui.table_next_column() + imgui.text(tn); imgui.table_next_column() + curr_w = app._editing_bias_profile_tool_weights.get(tn, 3); imgui.set_next_item_width(-1) ch_w, n_w = imgui.slider_int(f"##bw_{tn}", curr_w, 1, 10); if ch_w: app._editing_bias_profile_tool_weights[tn] = n_w imgui.end_table() @@ -3172,7 +3082,8 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N imgui.end_child() if app._bias_cats_open: imgui.button("###bias_splitter", imgui.ImVec2(-1, 4)) - if imgui.is_item_active(): app._bias_split_v = max(0.1, min(0.9, app._bias_split_v + imgui.get_io().mouse_delta.y / rem_bias_y)) + if imgui.is_item_active(): + app._bias_split_v = max(0.1, min(0.9, app._bias_split_v + imgui.get_io().mouse_delta.y / rem_bias_y)) opened_bc = imgui.collapsing_header("Category Multipliers", imgui.TreeNodeFlags_.default_open) if opened_bc != app._bias_cats_open: app._bias_cats_open = opened_bc @@ -3180,10 +3091,12 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N imgui.begin_child("bcat_scroll", imgui.ImVec2(0, bh2), True) if True: if imgui.begin_table("bcats", 2): - imgui.table_setup_column("C", imgui.TableColumnFlags_.width_fixed, 220); imgui.table_setup_column("M", imgui.TableColumnFlags_.width_stretch) + imgui.table_setup_column("C", imgui.TableColumnFlags_.width_fixed, 220) + imgui.table_setup_column("M", imgui.TableColumnFlags_.width_stretch) for cn in sorted(models.DEFAULT_TOOL_CATEGORIES.keys()): - imgui.table_next_row(); imgui.table_next_column(); imgui.text(cn); imgui.table_next_column() - curr_m = app._editing_bias_profile_category_multipliers.get(cn, 1.0); imgui.set_next_item_width(-1) + imgui.table_next_row(); imgui.table_next_column() + imgui.text(cn); imgui.table_next_column() + curr_m = app._editing_bias_profile_category_multipliers.get(cn, 1.0); imgui.set_next_item_width(-1) ch_m, n_m = imgui.slider_float(f"##cm_{cn}", curr_m, 0.1, 5.0, "%.1fx"); if ch_m: app._editing_bias_profile_category_multipliers[cn] = n_m imgui.end_table() @@ -3202,24 +3115,25 @@ def render_tool_preset_manager_content(app: App, is_embedded: bool = False) -> N # --- Footer Buttons --- imgui.separator() if imgui.button("Save##tp", imgui.ImVec2(100, 0)): - if app._editing_tool_preset_name.strip(): app.controller._cb_save_tool_preset(app._editing_tool_preset_name.strip(), app._editing_tool_preset_categories, app._editing_tool_preset_scope); app.ai_status = f"Saved: {app._editing_tool_preset_name}" + if app._editing_tool_preset_name.strip(): + app.controller._cb_save_tool_preset(app._editing_tool_preset_name.strip(), app._editing_tool_preset_categories, app._editing_tool_preset_scope) + app.ai_status = f"Saved: {app._editing_tool_preset_name}" imgui.same_line() if imgui.button("Delete##tp", imgui.ImVec2(100, 0)): - if app._editing_tool_preset_name: app.controller._cb_delete_tool_preset(app._editing_tool_preset_name, app._editing_tool_preset_scope); app._editing_tool_preset_name = ""; app._selected_tool_preset_idx = -1 + if app._editing_tool_preset_name: + app.controller._cb_delete_tool_preset(app._editing_tool_preset_name, app._editing_tool_preset_scope) + app._editing_tool_preset_name = "" + app._selected_tool_preset_idx = -1 imgui.same_line() if not is_embedded: - if imgui.button("Close##tp", imgui.ImVec2(100, 0)): app.show_tool_preset_manager_window = False + if imgui.button("Close##tp", imgui.ImVec2(100, 0)): + app.show_tool_preset_manager_window = False imgui.end_table() def render_tool_preset_manager_window(app: App, is_embedded: bool = False) -> None: - """ - Renders the window container for the Tool Preset Manager. + """Renders the window container for the Tool Preset Manager. - State Mutations: - app.show_tool_preset_manager_window (visibility toggle) - - SSDL Shape: - `[I] -> [I:window] => [I:tool_preset_manager_content]` + SSDL: `[I] -> [I:window] => [I:tool_preset_manager_content]` """ if not app.show_tool_preset_manager_window and not is_embedded: return if not is_embedded: @@ -3231,20 +3145,10 @@ def render_tool_preset_manager_window(app: App, is_embedded: bool = False) -> No render_tool_preset_manager_content(app, is_embedded=is_embedded) def render_persona_editor_window(app: App, is_embedded: bool = False) -> None: - """ - Renders the Persona Editor window, allowing creating, deleting, and modifying persona settings + """Renders the Persona Editor window, allowing creating, deleting, and modifying persona settings (prompts, preferred models list, tool presets, aggregation strategy, etc). - State Mutations: - app.show_persona_editor_window (visibility toggle) - app._editing_persona_name, app._editing_persona_system_prompt - app._editing_persona_tool_preset_id, app._editing_persona_bias_profile_id - app._editing_persona_context_preset_id, app._editing_persona_aggregation_strategy - app._editing_persona_preferred_models_list, app._editing_persona_is_new - app.controller (saves/deletes personas) - - SSDL Shape: - `[I:personas_list] -> [B:new_persona] -> [I:settings_editor] -> [I:models_list] -> [I:system_prompt_box] => [B:save_delete]` + SSDL: `[I:personas_list] -> [B:new_persona] -> [I:settings_editor] -> [I:models_list] -> [I:system_prompt_box] => [B:save_delete]` """ if not app.show_persona_editor_window and not is_embedded: return if not is_embedded: @@ -3263,22 +3167,23 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None: imgui.begin_child("persona_list_pane", imgui.ImVec2(0, 0), False) if True: if imgui.button("New Persona", imgui.ImVec2(-1, 0)): - app._editing_persona_name = ""; app._editing_persona_system_prompt = "" - app._editing_persona_tool_preset_id = ""; app._editing_persona_bias_profile_id = "" - app._editing_persona_context_preset_id = "" - app._editing_persona_aggregation_strategy = "" + app._editing_persona_name = ""; app._editing_persona_system_prompt = "" + app._editing_persona_tool_preset_id = ""; app._editing_persona_bias_profile_id = "" + app._editing_persona_context_preset_id = "" + app._editing_persona_aggregation_strategy = "" app._editing_persona_preferred_models_list = [{"provider": app.current_provider, "model": app.current_model, "temperature": 0.7, "top_p": 1.0, "max_output_tokens": 4096, "history_trunc_limit": 900000}] - app._editing_persona_scope = "project"; app._editing_persona_is_new = True + app._editing_persona_scope = "project"; app._editing_persona_is_new = True imgui.separator() personas = getattr(app.controller, 'personas', {}) for name in sorted(personas.keys()): if name and imgui.selectable(f"{name}##p_list", name == app._editing_persona_name and not getattr(app, '_editing_persona_is_new', False))[0]: + import copy; #TODO(Ed): Review local import p = personas[name]; app._editing_persona_name = p.name; app._editing_persona_system_prompt = p.system_prompt or "" - app._editing_persona_tool_preset_id = p.tool_preset or ""; app._editing_persona_bias_profile_id = p.bias_profile or "" - app._editing_persona_context_preset_id = getattr(p, 'context_preset', '') or "" - app._editing_persona_aggregation_strategy = getattr(p, 'aggregation_strategy', '') or "" - import copy; app._editing_persona_preferred_models_list = copy.deepcopy(p.preferred_models) if p.preferred_models else [] - app._editing_persona_scope = app.controller.persona_manager.get_persona_scope(p.name); app._editing_persona_is_new = False + app._editing_persona_tool_preset_id = p.tool_preset or ""; app._editing_persona_bias_profile_id = p.bias_profile or "" + app._editing_persona_context_preset_id = getattr(p, 'context_preset', '') or "" + app._editing_persona_aggregation_strategy = getattr(p, 'aggregation_strategy', '') or "" + app._editing_persona_preferred_models_list = copy.deepcopy(p.preferred_models) if p.preferred_models else [] + app._editing_persona_scope = app.controller.persona_manager.get_persona_scope(p.name); app._editing_persona_is_new = False imgui.end_child() # --- Right Editor --- @@ -3287,11 +3192,12 @@ 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) - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Name:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, app._editing_persona_name = imgui.input_text("##pname", app._editing_persona_name, 128) + imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 60); imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Name:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, app._editing_persona_name = imgui.input_text("##pname", app._editing_persona_name, 128) imgui.table_next_row(); imgui.table_next_column(); imgui.text("Scope:"); imgui.table_next_column() if imgui.radio_button("Global##pscope", getattr(app, '_editing_persona_scope', 'project') == "global"): app._editing_persona_scope = "global" imgui.same_line(); @@ -3300,9 +3206,9 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None: rem_y = imgui.get_content_region_avail().y - 100 if app._persona_models_open and app._persona_prompt_open: h1, h2 = rem_y * app._persona_split_v, rem_y - (rem_y * app._persona_split_v) - 10 - elif app._persona_models_open: h1, h2 = rem_y, 0 - elif app._persona_prompt_open: h1, h2 = 0, rem_y - else: h1, h2 = 0, 0 + elif app._persona_models_open: h1, h2 = rem_y, 0 + elif app._persona_prompt_open: h1, h2 = 0, rem_y + else: h1, h2 = 0, 0 imgui.dummy(imgui.ImVec2(0, 4)) opened_models = imgui.collapsing_header("Preferred Models", imgui.TreeNodeFlags_.default_open) @@ -3328,20 +3234,24 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None: with imscope.indent(20): if imgui.begin_table("model_settings", 2, imgui.TableFlags_.borders_inner_v): imgui.table_setup_column("Label", imgui.TableColumnFlags_.width_fixed, 120); imgui.table_setup_column("Control", imgui.TableColumnFlags_.width_stretch) - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Provider:"); imgui.table_next_column(); imgui.set_next_item_width(-1) - p_idx = providers.index(prov) + 1 if prov in providers else 0; ch_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers) + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Provider:"); imgui.table_next_column(); + imgui.set_next_item_width(-1) + p_idx = providers.index(prov) + 1 if prov in providers else 0; + ch_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers) if ch_p: entry["provider"] = providers[p_idx-1] if p_idx > 0 else "" - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Model:"); imgui.table_next_column(); imgui.set_next_item_width(-1) - m_list = app.controller.all_available_models.get(entry.get("provider", ""), []); m_idx = m_list.index(mod) + 1 if mod in m_list else 0 + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Model:"); imgui.table_next_column(); + imgui.set_next_item_width(-1) + m_list = app.controller.all_available_models.get(entry.get("provider", ""), []) + m_idx = m_list.index(mod) + 1 if mod in m_list else 0 ch_m, m_idx = imgui.combo("##model", m_idx, ["None"] + m_list) if ch_m: entry["model"] = m_list[m_idx-1] if m_idx > 0 else "" imgui.table_next_row(); imgui.table_next_column(); imgui.text("Temperature:"); imgui.table_next_column(); cw = imgui.get_content_region_avail().x - imgui.set_next_item_width(cw * 0.7); _, entry["temperature"] = imgui.slider_float("##ts", entry.get("temperature", 0.7), 0.0, 2.0, "%.1f") + imgui.set_next_item_width(cw * 0.7); _, entry["temperature"] = imgui.slider_float("##ts", entry.get("temperature", 0.7), 0.0, 2.0, "%.1f") imgui.same_line(); imgui.set_next_item_width(-1); _, entry["temperature"] = imgui.input_float("##ti", entry.get("temperature", 0.7), 0.1, 0.1, "%.1f") imgui.table_next_row(); imgui.table_next_column(); imgui.text("Top-P:"); imgui.table_next_column() - imgui.set_next_item_width(cw * 0.7); _, entry["top_p"] = imgui.slider_float("##tp_s", entry.get("top_p", 1.0), 0.0, 1.0, "%.2f") + imgui.set_next_item_width(cw * 0.7); _, entry["top_p"] = imgui.slider_float("##tp_s", entry.get("top_p", 1.0), 0.0, 1.0, "%.2f") imgui.same_line(); imgui.set_next_item_width(-1); _, entry["top_p"] = imgui.input_float("##tp_i", entry.get("top_p", 1.0), 0.05, 0.05, "%.2f") - imgui.table_next_row(); imgui.table_next_column(); imgui.text("Max Tokens:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, entry["max_output_tokens"] = imgui.input_int("##maxt", entry.get("max_output_tokens", 4096)) + imgui.table_next_row(); imgui.table_next_column(); imgui.text("Max Tokens:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, entry["max_output_tokens"] = imgui.input_int("##maxt", entry.get("max_output_tokens", 4096)) imgui.table_next_row(); imgui.table_next_column(); imgui.text("History Limit:"); imgui.table_next_column(); imgui.set_next_item_width(-1); _, entry["history_trunc_limit"] = imgui.input_int("##hist", entry.get("history_trunc_limit", 900000)) imgui.end_table() imgui.pop_id() @@ -3421,12 +3331,10 @@ def render_persona_editor_window(app: App, is_embedded: bool = False) -> None: #region: Context Management def render_files_and_media(app: App) -> None: - """ - Renders the inventory of files and screenshots. Allows adding files or directories to the inventory + """Renders the inventory of files and screenshots. Allows adding files or directories to the inventory and attaching files or screenshots to the active context. - SSDL Shape: - `[I:inventory] -> [B:add_files_folders] -> [I:screenshots] => [B:add_screenshots]` + SSDL: `[I:inventory] -> [B:add_files_folders] -> [I:screenshots] => [B:add_screenshots]` ASCII Layout Map: +---------------------------------------------------------+ @@ -3446,11 +3354,11 @@ def render_files_and_media(app: App) -> None: if imgui.collapsing_header("Files", imgui.TreeNodeFlags_.default_open): with imscope.group(): if imgui.begin_table("files_table", 3, imgui.TableFlags_.resizable | imgui.TableFlags_.borders | imgui.TableFlags_.row_bg): - imgui.table_setup_column("Act", imgui.TableColumnFlags_.width_fixed, 60) - imgui.table_setup_column("Path", imgui.TableColumnFlags_.width_stretch) + imgui.table_setup_column("Act", imgui.TableColumnFlags_.width_fixed, 60) + imgui.table_setup_column("Path", imgui.TableColumnFlags_.width_stretch) imgui.table_setup_column("Status", imgui.TableColumnFlags_.width_fixed, 70) imgui.table_headers_row() - + to_remove_idx = -1 app.files.sort(key=lambda f: f.path.lower() if hasattr(f, 'path') else str(f).lower()) file_indices = {id(f): idx for idx, f in enumerate(app.files)} @@ -3465,7 +3373,7 @@ def render_files_and_media(app: App) -> None: fpath = f_item.path if hasattr(f_item, 'path') else str(f_item) in_context = any((cf.path if hasattr(cf, 'path') else str(cf)) == fpath for cf in app.context_files) is_cached = any(fpath in c for c in getattr(app, '_cached_files', [])) - + if imgui.button(f"+##add_f_{i}"): if not in_context: from src import models @@ -3476,18 +3384,15 @@ def render_files_and_media(app: App) -> None: imgui.same_line() if imgui.button(f"x##rem_f_{i}"): to_remove_idx = i - + imgui.table_set_column_index(1) imgui.text(fpath) if imgui.is_item_hovered(): imgui.set_tooltip(fpath) imgui.table_set_column_index(2) - if in_context: - imgui.text_colored(theme.get_color("status_success"), "Active") - elif is_cached: - imgui.text_colored(theme.get_color("status_info"), "Cached") - else: - imgui.text_disabled(" - ") + if in_context: imgui.text_colored(theme.get_color("status_success"), "Active") + elif is_cached: imgui.text_colored(theme.get_color("status_info"), "Cached") + else: imgui.text_disabled(" - ") imgui.end_table() if to_remove_idx != -1: app.files.pop(to_remove_idx) @@ -3530,12 +3435,10 @@ def render_files_and_media(app: App) -> None: return def render_context_batch_actions(app: App, total_lines: int, total_ast: int) -> None: - """ - Renders a batch actions control bar. Allows batch-changing view modes of selected files, + """Renders a batch actions control bar. Allows batch-changing view modes of selected files, selecting/deselecting all, adding files, and generating context preview markdown. - SSDL Shape: - `[I:context_files] -> [B:mode_batch_buttons] -> [B:selection_buttons] -> [B:all_add_del] => [B:preview]` + SSDL: `[I:context_files] -> [B:mode_batch_buttons] -> [B:selection_buttons] -> [B:all_add_del] => [B:preview]` ASCII Layout Map: +---------------------------------------------------------+ @@ -3593,12 +3496,10 @@ def render_context_batch_actions(app: App, total_lines: int, total_ast: int) -> imgui.text(f" | Total: {len(app.context_files)} files, {total_lines} lines, {total_ast} AST elements") def render_add_context_files_modal(app: App) -> None: - """ - Renders a modal popup listing files in the project inventory that can be batch-added + """Renders a modal popup listing files in the project inventory that can be batch-added to the active context. - SSDL Shape: - `[I:picker_list] -> [B:checkboxes] -> [B:add_selected_button] => [B:cancel_button]` + SSDL: `[I:picker_list] -> [B:checkboxes] -> [B:add_selected_button] => [B:cancel_button]` ASCII Layout Map: +---------------------------------------------------------+ @@ -3649,12 +3550,10 @@ def render_add_context_files_modal(app: App) -> None: imgui.end_popup() def render_context_composition_panel(app: App) -> None: - """ - Renders the Context Composition panel containing loaded project files, presets, + """Renders the Context Composition panel containing loaded project files, presets, and visual screenshot files. Displays token stats, batch files actions, and collapsible trees. - SSDL Shape: - `[I:stats] -> [I:batch_actions] -> [I:files_table] -> [I:presets] -> [I:screenshots]` + SSDL: `[I:stats] -> [I:batch_actions] -> [I:files_table] -> [I:presets] -> [I:screenshots]` ASCII Layout Map: +-------------------------------------------------------------+ @@ -7275,13 +7174,11 @@ def render_task_dag_panel(app: App) -> None: # 4. Task DAG Visualizer imgui.text_disabled("No active MMA track or tickets.") def render_beads_tab(app: App) -> None: - """ - Renders the Beads Graph tab. Checks for `dolt` and `bd` CLI availability, + """Renders the Beads Graph tab. Checks for `dolt` and `bd` CLI availability, shows a warning if missing, then lists beads from the Dolt-backed BeadsClient in a 3-column table (ID / Status / Title). - SSDL Shape: - `[I:dep_check] -> [B:refresh] -> [I:beads_table]` + SSDL Shape: `[I:dep_check] -> [B:refresh] -> [I:beads_table]` ASCII Layout Map: +---------------------------------------------------------+ @@ -7299,22 +7196,22 @@ def render_beads_tab(app: App) -> None: if imgui.button("Refresh Beads"): pass imgui.separator() - + # Check for dolt/bd dependencies dolt_path = shutil.which("dolt") - bd_path = shutil.which("bd") + bd_path = shutil.which("bd") if not dolt_path or not bd_path: missing = [] if not dolt_path: missing.append("'dolt'") - if not bd_path: missing.append("'bd'") + if not bd_path: missing.append("'bd'") imgui.text_colored(theme.get_color("status_warning"), f"Warning: {', '.join(missing)} not found in PATH.") imgui.text_wrapped("Beads mode requires Dolt and the Beads (bd) CLI tools.") - + if getattr(app, "ui_project_execution_mode", "native") == "beads": try: from src import beads_client bclient = beads_client.BeadsClient(Path(app.active_project_root)) - beads = bclient.list_beads() + beads = bclient.list_beads() if not beads: imgui.text_disabled("No beads found.") else: @@ -7324,24 +7221,18 @@ def render_beads_tab(app: App) -> None: imgui.table_setup_column("Title") imgui.table_headers_row() for b in beads: - imgui.table_next_row() - imgui.table_next_column() - imgui.text(str(b.id)) - imgui.table_next_column() - imgui.text(str(b.status)) - imgui.table_next_column() - imgui.text(str(b.title)) + imgui.table_next_row(); imgui.table_next_column() + imgui.text(str(b.id)); imgui.table_next_column() + imgui.text(str(b.status)); imgui.table_next_column(); imgui.text(str(b.title)) imgui.end_table() except Exception as e: imgui.text_colored(theme.get_color("status_error"), f"Error loading beads: {e}") def render_mma_focus_selector(app: App) -> None: - """ - Renders the Focus Agent selector. Filters the discussion entries list to show + """Renders the Focus Agent selector. Filters the discussion entries list to show only the output of the chosen tier agent (or All). Includes a clear [x] button. - SSDL Shape: - `[I:focus_combo] -> [B:clear_x?]` + SSDL: `[I:focus_combo] -> [B:clear_x?]` ASCII Layout Map: +---------------------------------------------------------+