diff --git a/src/gui_2.py b/src/gui_2.py index 6455aa5b..a79c4bb0 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -22,7 +22,7 @@ _thirdparty = os.path.join(_project_root, "thirdparty") if _thirdparty not in sys.path: sys.path.insert(0, _thirdparty) -from contextlib import ExitStack, nullcontext +from contextlib import ExitStack, nullcontext from pathlib import Path from typing import Optional, Any from imgui_bundle import imgui, hello_imgui, immapp, imgui_node_editor as ed, imgui_color_text_edit as ced @@ -41,6 +41,7 @@ import importlib as _importlib from typing import Any as _Any from typing import Optional as _Optional +#TODO(Ed): Remove Excpetion based errors class _LazyModule: """Lazy proxy that defers an import until first attribute access or call. @@ -89,11 +90,10 @@ class _FiledialogStub: def askdirectory(self, *args: _Any, **kwargs: _Any) -> str: return "" def asksaveasfilename(self, *args: _Any, **kwargs: _Any) -> str: return "" - # Heavy modules that were previously top-level imports (now lazy): -np = _LazyModule("numpy") # was: import numpy as np +np = _LazyModule("numpy") # was: import numpy as np filedialog = _LazyModule("tkinter", "filedialog") # was: from tkinter import filedialog -Tk = _LazyModule("tkinter", "Tk") # was: from tkinter import Tk +Tk = _LazyModule("tkinter", "Tk") # was: from tkinter import Tk from src.diff_viewer import apply_patch_to_file from src import ai_client @@ -186,6 +186,7 @@ def _detect_refresh_rate_win32() -> float: shelled out to PowerShell + WMI (Get-CimInstance Win32_VideoController), which cost ~350ms on every startup and blocked the first frame. """ + #Note(Ed): Exception(Thirdparty) try: import ctypes from ctypes import wintypes @@ -233,6 +234,7 @@ def _resolve_font_path(font_path: str, assets_dir: Path) -> str: p = Path(font_path) if not p.is_absolute(): return font_path # already relative; hello_imgui searches the assets folder + #Note(Ed): Exception(Thirdparty) try: if p.is_relative_to(assets_dir): return str(p.relative_to(assets_dir)).replace("\\", "/") @@ -285,7 +287,7 @@ def _render_v2_capability_badges(caps: "VendorCapabilities") -> None: ] enabled: list[tuple[str, str]] = [] for field_name, label in badged_fields: - if getattr(caps, field_name, False): + if getattr(caps, field_name, False): enabled.append((field_name, label)) if not enabled: return imgui.text("Capabilities") @@ -297,16 +299,9 @@ class App: """The main ImGui interface orchestrator for Manual Slop.""" def __init__(self) -> None: - """ - Initializes core app dependencies (controller, history, performance monitor, + """Initializes core app dependencies (controller, history, performance monitor, command palette, workspace manager) and registers app callback handlers. - - State Mutations: - self.controller, self.perf_monitor, self.history, - self.show_command_palette, self.workspace_manager. - - SSDL Shape: - `[I:init_controller] -> [I:init_workspace] -> [I:load_profiles]` + SSDL Shape: `[I:init_controller] -> [I:init_workspace] -> [I:load_profiles]` """ #region: --- Core Dependencies & State --- from src.startup_profiler import startup_profiler @@ -505,22 +500,22 @@ class App: self.node_editor_ctx = ed.create_editor(self.node_editor_config) # --- Context & AST State --- - self.ui_selected_ticket_id: Optional[str] = None - self.ui_selected_tickets: set[str] = set() - self.ui_selected_context_files: set[str] = set() - self.ui_new_ticket_priority: str = 'medium' - self._autofocus_response_tab = False + self.ui_selected_ticket_id: Optional[str] = None + self.ui_selected_tickets: set[str] = set() + self.ui_selected_context_files: set[str] = set() + self.ui_new_ticket_priority: str = 'medium' + self._autofocus_response_tab = False self._last_selected_context_index = -1 - self.ui_inspecting_ast_file = None - self._show_ast_inspector = False - self._cached_ast_nodes = [] - self._cached_ast_file_path = '' - self._cached_ast_file_lines = [] - self.ui_editing_slices_file = None - self._slice_sel_start = -1 - self._slice_sel_end = -1 - self.context_files = [] - self.ui_synthesis_prompt: str = "" + self.ui_inspecting_ast_file = None + self._show_ast_inspector = False + self._cached_ast_nodes = [] + self._cached_ast_file_path = '' + self._cached_ast_file_lines = [] + self.ui_editing_slices_file = None + self._slice_sel_start = -1 + self._slice_sel_end = -1 + self.context_files = [] + self.ui_synthesis_prompt: str = "" self.ui_synthesis_selected_takes: dict[str, bool] = {} # --- Rendering & Theme State --- @@ -564,18 +559,17 @@ class App: # is safe. The render_warmup_status_indicator() function reads # the timestamp to show a brief "ready" tag for 3 seconds. if hasattr(self.controller, "on_warmup_complete"): + #Note(Ed): Exception(Thirdparty) try: self.controller.on_warmup_complete(lambda status: _on_warmup_complete_callback(self, status)) except Exception: pass self._diag_layout_state() def _diag_layout_state(self) -> None: - """ - One-shot startup diagnostic: log show_windows state and warn if the + """One-shot startup diagnostic: log show_windows state and warn if the on-disk manualslop_layout.ini references window names that no longer exist in the current code. Helps users and test operators detect stale layout state at a glance instead of debugging missing panels. - [C: src/gui_2.py:App._post_init] """ import os as _os visible_by_default = [w for w, v in self.show_windows.items() if v] @@ -588,6 +582,7 @@ class App: return ini_size = _os.path.getsize(ini_path) sys.stderr.write(f"[GUI] layout file: {ini_path} ({ini_size} bytes)\n") + #Note(Ed): Exception(Thirdparty) try: with open(ini_path, encoding="utf-8") as _f: _ini_text = _f.read() @@ -611,14 +606,9 @@ class App: return result def run(self) -> None: - """ - Initializes the ImGui runner (HelloImGui) and starts the main application loop. + """Initializes the ImGui runner (HelloImGui) and starts the main application loop. Loads system themes, default styling metrics, fonts, and sets up window docking layouts. - - SSDL Shape: - `[I:hello_imgui] -> o-> [I:main_loop]` - - [C: simulation/sim_base.py:run_sim, src/mcp_client.py:get_git_diff, src/project_manager.py:get_git_commit, src/rag_engine.py:RAGEngine._search_mcp, src/shell_runner.py:run_powershell, tests/conftest.py:kill_process_tree, tests/conftest.py:live_gui, tests/test_conductor_abort_event.py:test_conductor_abort_event_populated, tests/test_conductor_engine_v2.py:test_conductor_engine_dynamic_parsing_and_execution, tests/test_conductor_engine_v2.py:test_conductor_engine_run_executes_tickets_in_order, tests/test_extended_sims.py:test_ai_settings_sim_live, tests/test_extended_sims.py:test_context_sim_live, tests/test_extended_sims.py:test_execution_sim_live, tests/test_extended_sims.py:test_tools_sim_live, tests/test_external_editor_gui.py:get_vscode_processes, tests/test_external_editor_gui.py:test_vscode_launches_with_diff_view, tests/test_gui_custom_window.py:test_app_window_is_borderless, tests/test_headless_simulation.py:module, tests/test_headless_verification.py:test_headless_verification_error_and_qa_interceptor, tests/test_headless_verification.py:test_headless_verification_full_run, tests/test_mock_gemini_cli.py:run_mock, tests/test_orchestration_logic.py:test_conductor_engine_run, tests/test_parallel_execution.py:test_conductor_engine_pool_integration, tests/test_sim_ai_settings.py:test_ai_settings_simulation_run, tests/test_sim_context.py:test_context_simulation_run, tests/test_sim_execution.py:test_execution_simulation_run, tests/test_sim_tools.py:test_tools_simulation_run] + SSDL: `[I:hello_imgui] -> o-> [I:main_loop]` """ if "--headless" in sys.argv: print("Headless mode active") @@ -642,15 +632,15 @@ class App: # (removed stale _t-based print; the phase() above already logs RunnerParams_init) if sys.platform == "win32": - self.runner_params.app_window_params.borderless = True - self.runner_params.app_window_params.borderless_closable = False - self.runner_params.app_window_params.borderless_movable = False + self.runner_params.app_window_params.borderless = True + self.runner_params.app_window_params.borderless_closable = False + self.runner_params.app_window_params.borderless_movable = False self.runner_params.app_window_params.borderless_resizable = True - self.runner_params.app_window_params.window_geometry.size = (1680, 1200) - self.runner_params.imgui_window_params.enable_viewports = getattr(self, "ui_multi_viewport", False) - self.runner_params.imgui_window_params.remember_theme = True - self.runner_params.imgui_window_params.tweaked_theme = theme.get_tweaked_theme() + self.runner_params.app_window_params.window_geometry.size = (1680, 1200) + self.runner_params.imgui_window_params.enable_viewports = getattr(self, "ui_multi_viewport", False) + self.runner_params.imgui_window_params.remember_theme = True + self.runner_params.imgui_window_params.tweaked_theme = theme.get_tweaked_theme() self.runner_params.imgui_window_params.default_imgui_window_type = hello_imgui.DefaultImGuiWindowType.provide_full_screen_dock_space # Enforce DPI Awareness and User Scale @@ -667,25 +657,26 @@ class App: # Enable idling with monitor refresh rate to effectively cap FPS self.runner_params.fps_idling.enable_idling = True - self.runner_params.fps_idling.fps_idle = fps_cap + self.runner_params.fps_idling.fps_idle = fps_cap - self.runner_params.imgui_window_params.show_menu_bar = True + self.runner_params.imgui_window_params.show_menu_bar = True self.runner_params.imgui_window_params.show_menu_view_themes = True - self.runner_params.ini_folder_type = hello_imgui.IniFolderType.current_folder - self.runner_params.ini_filename = "manualslop_layout.ini" + self.runner_params.ini_folder_type = hello_imgui.IniFolderType.current_folder + self.runner_params.ini_filename = "manualslop_layout.ini" def _profiled_setup_style() -> None: with startup_profiler.phase("setup_imgui_style"): theme.apply_current() def _profiled_post_init() -> None: with startup_profiler.phase("post_init"): self._post_init() - self.runner_params.callbacks.show_gui = self._gui_func - self.runner_params.callbacks.show_menus = self._show_menus + self.runner_params.callbacks.show_gui = self._gui_func + self.runner_params.callbacks.show_menus = self._show_menus self.runner_params.callbacks.load_additional_fonts = self._load_fonts - self.runner_params.callbacks.setup_imgui_style = _profiled_setup_style - self.runner_params.callbacks.post_init = _profiled_post_init + self.runner_params.callbacks.setup_imgui_style = _profiled_setup_style + self.runner_params.callbacks.post_init = _profiled_post_init self._fetch_models(self.current_provider) md_options = markdown_helper.get_renderer().options + #Note(Ed): Exception(Thirdparty) try: immapp.run(self.runner_params, add_ons_params=immapp.AddOnsParams(with_markdown_options=md_options)) except RuntimeError as _immapp_exc: @@ -703,8 +694,8 @@ class App: self.controller._last_imgui_assert = traceback.format_exc() print( f"[GUI-DEGRADED] immapp.run raised: {_immapp_exc}", - file=sys.stderr, - flush=True, + file = sys.stderr, + flush = True, ) print(self.controller._last_imgui_assert if hasattr(self, "controller") and self.controller else "", file=sys.stderr, flush=True) @@ -722,7 +713,7 @@ class App: hello_imgui.set_assets_folder(str(assets_dir.absolute())) # Improved font rendering with oversampling - config = imgui.ImFontConfig() + config = imgui.ImFontConfig() config.oversample_h = 3 config.oversample_v = 3 @@ -730,6 +721,7 @@ class App: if font_path: font_path = _resolve_font_path(font_path, assets_dir) + #Note(Ed): Exception(Thirdparty) # Just try loading it directly; hello_imgui will look in the assets folder try: with startup_profiler.phase("load_fonts.main_with_fontawesome"): @@ -739,10 +731,11 @@ class App: self.main_font = None else: self.main_font = None - + + #Note(Ed): Exception(Thirdparty) try: with startup_profiler.phase("load_fonts.mono"): - params = hello_imgui.FontLoadingParams(font_config=config) + params = hello_imgui.FontLoadingParams(font_config=config) self.mono_font = hello_imgui.load_font("fonts/MapleMono-Regular.ttf", font_size, params) except Exception as e: print(f"Failed to load mono font: {e}") @@ -756,6 +749,7 @@ class App: """UI-level wrapper for approving a pending MMA sub-agent spawn.""" self._handle_mma_respond(approved=True) + #TODO(Ed): Remove Exception based errors. def __getattr__(self, name: str) -> Any: if name == 'controller': raise AttributeError(name) @@ -772,7 +766,7 @@ class App: def _handle_generate_send(self) -> None: if not self.ui_selected_context_files and not getattr(self, "_pending_proceed_generate", False): self._pending_generation_action = 'generate' - self.show_empty_context_modal = True + self.show_empty_context_modal = True else: self._pending_proceed_generate = False self.controller._handle_generate_send() @@ -780,13 +774,13 @@ class App: def _handle_md_only(self) -> None: if not self.ui_selected_context_files and not getattr(self, "_pending_proceed_md_only", False): self._pending_generation_action = 'md_only' - self.show_empty_context_modal = True + self.show_empty_context_modal = True else: self._pending_proceed_md_only = False self.controller._handle_md_only() @property - def current_provider(self) -> str: + def current_provider(self) -> str: return self.controller.current_provider @current_provider.setter @@ -801,8 +795,10 @@ class App: def current_model(self, value: str) -> None: self.controller.current_model = value + #TODO(Ed): Remove Exception based errors. def _get_active_capabilities(self) -> "VendorCapabilities": from src.vendor_capabilities import VendorCapabilities, get_capabilities + #TODO(Ed): Remove Exception based errors. try: caps = get_capabilities(self.current_provider, self.current_model) except KeyError: @@ -824,27 +820,21 @@ class App: @property def app_debug_info(self) -> dict: return { - "context_files": [f.path for f in self.context_files], - "missing_files": self.missing_files, - "screenshots": self.screenshots, + "context_files": [f.path for f in self.context_files], + "missing_files": self.missing_files, + "screenshots": self.screenshots, "ui_new_context_preset_name": self.ui_new_context_preset_name, "target_context_preset_name": self.target_context_preset_name, - "show_missing_files_modal": self.show_missing_files_modal, - "active_project_root": str(self.controller.active_project_root), - "project_keys": list(self.controller.project.keys()), - "presets": list(self.controller.project.get('context_presets', {}).keys()) + "show_missing_files_modal": self.show_missing_files_modal, + "active_project_root": str(self.controller.active_project_root), + "project_keys": list(self.controller.project.keys()), + "presets": list(self.controller.project.get('context_presets', {}).keys()) } def _take_snapshot(self) -> history.UISnapshot: - """ - Captures the current state of UI input parameters, system prompts, active + """ Captures the current state of UI input parameters, system prompts, active discussions, and files list, returning a UISnapshot for history management. - - State Mutations: - None (read-only state capture). - - SSDL Shape: - `[Q:ui_state] -> [I:copy] -> [T:snapshot]` + SSDL: `[Q:ui_state] -> [I:copy] -> [T:snapshot]` """ from src import history import copy @@ -865,16 +855,9 @@ class App: ) def _apply_snapshot(self, snapshot: history.UISnapshot) -> None: - """ - Applies a previously captured UISnapshot back to the active UI state. + """Applies a previously captured UISnapshot back to the active UI state. Restores input fields, parameters, discussions, screenshots, and context files. - - State Mutations: - Modifies active UI variables (self.ui_ai_input, self.temperature, self.files, self.context_files, etc.) - self._is_applying_snapshot (temporarily set to True) - - SSDL Shape: - `[I:lock_flag] -> [S:ui_state] -> [I:unlock]` + SSDL Shape: `[I:lock_flag] -> [S:ui_state] -> [I:unlock]` """ self._is_applying_snapshot = True try: @@ -893,42 +876,31 @@ class App: from src import models self.files = [] for f in snapshot.files: - if isinstance(f, dict): - self.files.append(models.FileItem.from_dict(f)) - else: - self.files.append(models.FileItem(path=str(f))) + if isinstance(f, dict): self.files.append(models.FileItem.from_dict(f)) + else: self.files.append(models.FileItem(path=str(f))) self.context_files = [] for f in snapshot.context_files: - if isinstance(f, dict): - self.context_files.append(models.FileItem.from_dict(f)) - else: - self.context_files.append(models.FileItem(path=str(f))) + if isinstance(f, dict): self.context_files.append(models.FileItem.from_dict(f)) + else: self.context_files.append(models.FileItem(path=str(f))) - self.screenshots = list(snapshot.screenshots) + self.screenshots = list(snapshot.screenshots) self._last_ui_snapshot = snapshot # Update last snapshot to avoid immediate re-push finally: - self._is_applying_snapshot = False + self._is_applying_snapshot = False # ?? TODO(Ed): Whats the point of this?? def _capture_workspace_profile(self, name: str) -> models.WorkspaceProfile: - """ - Serializes the current window visibility states, popped-out panel layouts, and + """Serializes the current window visibility states, popped-out panel layouts, and ImGui INI configurations into a WorkspaceProfile object. - - State Mutations: - self._ini_capture_ready (set to True on first invocation to bypass initial ImGui frame bugs). - - SSDL Shape: - `[Q:ui_states] -> [B:ini_ready] -> [T:profile]` + SSDL Shape: `[Q:ui_states] -> [B:ini_ready] -> [T:profile]` """ if not getattr(self, "_ini_capture_ready", False): self._ini_capture_ready = True ini = "" else: - try: - ini = str(imgui.save_ini_settings_to_memory() or "") - except Exception: - ini = "" + #Note(Ed): Thirdparty Exception + try: ini = str(imgui.save_ini_settings_to_memory() or "") + except Exception: ini = "" panel_states = { "ui_separate_context_preview": getattr(self, "ui_separate_context_preview", False), "ui_separate_message_panel": getattr(self, "ui_separate_message_panel", False), @@ -944,73 +916,46 @@ class App: "ui_discussion_split_h": getattr(self, "ui_discussion_split_h", 300.0), } return models.WorkspaceProfile( - name=name, - ini_content=ini, - show_windows=copy.deepcopy(self.show_windows), - panel_states=panel_states + name = name, + ini_content = ini, + show_windows = copy.deepcopy(self.show_windows), + panel_states = panel_states ) def _apply_workspace_profile(self, profile: models.WorkspaceProfile): - """ - Restores the window docking layout and popped-out panel visibility states + """Restores the window docking layout and popped-out panel visibility states from a saved WorkspaceProfile. - - State Mutations: - Modifies window visibility and panel configuration state variables. - - SSDL Shape: - `[I:load_ini] -> [S:ui_states]` + SSDL Shape: `[I:load_ini] -> [S:ui_states]` """ imgui.load_ini_settings_from_memory(profile.ini_content) self.show_windows.update(profile.show_windows) for k, v in profile.panel_states.items(): - if hasattr(self, k): - setattr(self, k, v) + if hasattr(self, k): setattr(self, k, v) def _handle_undo(self) -> None: - """ - Reverts the application UI state to the previous snapshot in the history stack. - - State Mutations: - self.history (mutated to record index changes) - Modifies active UI variables via _apply_snapshot() - + """Reverts the application UI state to the previous snapshot in the history stack. DAG Render Context: Called by: _gui_func() (via hotkey Ctrl+Z) or undo button click. Calls: _take_snapshot(), _apply_snapshot(), HistoryManager.undo() - - Threading & Safety: - Must run synchronously on the Main Thread. """ - sys.stderr.write(f"[DEBUG History] _handle_undo called. can_undo={self.history.can_undo}\n") - sys.stderr.flush() - if not self.history.can_undo: - return + sys.stderr.write(f"[DEBUG History] _handle_undo called. can_undo={self.history.can_undo}\n"); sys.stderr.flush() + if not self.history.can_undo: return current = self._take_snapshot() - entry = self.history.undo(current, "Undo Action") + entry = self.history.undo(current, "Undo Action") if entry: - sys.stderr.write(f"[DEBUG History] Undoing to: {entry.description}\n") - sys.stderr.flush() + sys.stderr.write(f"[DEBUG History] Undoing to: {entry.description}\n"); sys.stderr.flush() self._apply_snapshot(entry.state) def _handle_jump_to_history(self, index: int) -> None: - sys.stderr.write(f"[DEBUG History] Jumping to index {index}\n") - sys.stderr.flush() + sys.stderr.write(f"[DEBUG History] Jumping to index {index}\n"); sys.stderr.flush() current = self._take_snapshot() - entry = self.history.jump_to_undo(index, current, "Before Jump") + entry = self.history.jump_to_undo(index, current, "Before Jump") if entry: self._apply_snapshot(entry.state) def _handle_redo(self) -> None: - """ - Re-applies the next snapshot in the history stack (forward navigation). - - State Mutations: - self.history (mutated to record index changes) - Modifies active UI variables via _apply_snapshot() - - SSDL Shape: - `[I:snapshot] -> [B:history] => [I:state]` + """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() @@ -1024,17 +969,9 @@ class App: self._apply_snapshot(entry.state) def shutdown(self) -> None: - """ - Cleanly shuts down the app's background tasks, saves workspace layout configurations, + """Cleanly shuts down the app's background tasks, saves workspace layout configurations, forces a save of dirty registries/caches, and terminates the active thread pools. - - State Mutations: - runner_params settings are flushed to disk (imgui.save_ini_settings_to_disk). - - SSDL Shape: - `[I:save_ini] -> [I:controller_shutdown]` - - [C: tests/conftest.py:app_instance, tests/conftest.py:mock_app] + SSDL Shape: `[I:save_ini] -> [I:controller_shutdown]` """ try: if hasattr(self, 'runner_params') and self.runner_params.ini_filename: @@ -1044,29 +981,23 @@ class App: self.controller.shutdown() def load_context_preset(self, name: str) -> None: - """ - [C: tests/test_context_presets.py:test_load_context_preset, tests/test_context_presets.py:test_load_nonexistent_preset] - """ preset = self.controller.load_context_preset(name) from src import models import copy self.context_files = [] for f in preset.files: fi = models.FileItem(path=f.path, view_mode=f.view_mode) - fi.custom_slices = copy.deepcopy(f.custom_slices) if hasattr(f, 'custom_slices') else [] - fi.ast_mask = copy.deepcopy(f.ast_mask) if hasattr(f, 'ast_mask') else {} - fi.ast_signatures = getattr(f, 'ast_signatures', False) + fi.custom_slices = copy.deepcopy(f.custom_slices) if hasattr(f, 'custom_slices') else [] + fi.ast_mask = copy.deepcopy(f.ast_mask) if hasattr(f, 'ast_mask') else {} + fi.ast_signatures = getattr(f, 'ast_signatures', False) fi.ast_definitions = getattr(f, 'ast_definitions', False) self.context_files.append(fi) - self.screenshots = list(preset.screenshots) - self.ui_file_paths = [f.path for f in preset.files] + self.screenshots = list(preset.screenshots) + self.ui_file_paths = [f.path for f in preset.files] self.ui_screenshot_paths = list(preset.screenshots) self._update_context_file_stats() def delete_context_preset(self, name: str) -> None: - """ - [C: tests/test_context_presets.py:test_delete_context_preset, tests/test_context_presets.py:test_delete_nonexistent_preset_no_error] - """ self.controller.delete_context_preset(name) if getattr(self, "ui_active_context_preset", "") == name: self.ui_active_context_preset = "" @@ -1103,12 +1034,9 @@ class App: since the keyboard shortcut (Ctrl+Shift+P) cannot be simulated via the hook API.""" self.show_command_palette = not self.show_command_palette if self.show_command_palette: - if hasattr(self, '_command_palette_query'): - self._command_palette_query = "" - if hasattr(self, '_command_palette_selected'): - self._command_palette_selected = 0 - if hasattr(self, '_command_palette_input_focused'): - self._command_palette_input_focused = False + if hasattr(self, '_command_palette_query'): self._command_palette_query = "" + if hasattr(self, '_command_palette_selected'): self._command_palette_selected = 0 + if hasattr(self, '_command_palette_input_focused'): self._command_palette_input_focused = False def _test_callback_func_write_to_file(self, data: str) -> None: """A dummy function that a custom_callback would execute for testing.""" @@ -1118,17 +1046,11 @@ class App: f.write(data) def _gui_func(self) -> None: - """ - Main immediate-mode render loop callback executed on every frame. + """Main immediate-mode render loop callback executed on every frame. Dispatches keyboard shortcuts, renders the background shader, custom title bar, main dockspace, and handles popups/modals. - State Mutations: - self.show_command_palette (toggled via Ctrl+Shift+P) - self._hot_reload_error (updated on Ctrl+Alt+R) - - SSDL Shape: - `o-> [I:hotkeys] -> [I:title_bar] -> [I:main_interface] -> [I:modals]` + SSDL Shape: `o-> [I:hotkeys] -> [I:title_bar] -> [I:main_interface] -> [I:modals]` ASCII Layout Map: +---------------------------------------------------------+ @@ -1155,7 +1077,7 @@ class App: sys.stderr.write(f"[startup] first _gui_func entry at {(time.time() - init_ts) * 1000:.1f}ms after init (window/GL + font/style/post_init callbacks done)\n") sys.stderr.flush() except Exception: pass - + # One-shot: kick off the controller's heavy-module warmup on the shared # io_pool once the FIRST frame has actually been painted. Waiting one frame # keeps the ~2s of SDK C-extension imports from holding the GIL during @@ -1168,33 +1090,29 @@ class App: self._preload_started = True else: self._first_frame_painted = True - + io = imgui.get_io() - if io.key_ctrl and io.key_alt and imgui.is_key_down(imgui.Key.r): - self._trigger_hot_reload() + if io.key_ctrl and io.key_alt and imgui.is_key_down(imgui.Key.r): self._trigger_hot_reload() if (io.key_ctrl and io.key_shift and not io.key_alt and not io.key_super and imgui.is_key_pressed(imgui.Key.p)): self.show_command_palette = not self.show_command_palette if self.show_command_palette: - if hasattr(self, '_command_palette_query'): - self._command_palette_query = "" - if hasattr(self, '_command_palette_selected'): - self._command_palette_selected = 0 - + if hasattr(self, '_command_palette_query'): self._command_palette_query = "" + if hasattr(self, '_command_palette_selected'): self._command_palette_selected = 0 + render_custom_title_bar(self) render_shader_live_editor(self) render_history_window(self) pushed_prior_tint = False - + # Render background shader bg = bg_shader.get_bg() ws = imgui.get_io().display_size - if bg.enabled: - bg.render(ws.x, ws.y) + if bg.enabled: bg.render(ws.x, ws.y) theme.render_post_fx(ws.x, ws.y, self.ai_status, self.ui_crt_filter) - + if self.perf_profiling_enabled: self.perf_monitor.start_component("_gui_func") try: if self.is_viewing_prior_session: @@ -1205,11 +1123,9 @@ class App: except Exception as e: sys.stderr.write(f"ERROR in _gui_func: {e}\n") traceback.print_exc() - + self._handle_history_logic() - - if self.perf_profiling_enabled: - self.perf_monitor.end_component("_gui_func") + if self.perf_profiling_enabled: self.perf_monitor.end_component("_gui_func") return def _render_window_if_open(self, name: str, render_func: Callable[[], None], flag_condition: bool = True) -> None: @@ -1220,9 +1136,6 @@ class App: if exp: render_func() def _show_menus(self) -> None: - """ - [C: tests/test_gui_window_controls.py:test_gui_window_controls_minimize_maximize_close] - """ global win32gui, win32con if win32gui is None: import win32con @@ -1231,11 +1144,10 @@ class App: win32gui = win32gui with imscope.menu("manual slop") as (active): - if active and imgui.menu_item("Quit", "Ctrl+Q", False)[0]: - self.runner_params.app_shall_exit = True + if active and imgui.menu_item("Quit", "Ctrl+Q", False)[0]: self.runner_params.app_shall_exit = True with imscope.menu("Windows") as (active): if (active): - for w in self.show_windows.keys(): + for w in self.show_windows.keys(): _, self.show_windows[w] = imgui.menu_item(w, "", self.show_windows[w]) with imscope.menu("Project") as (active): if active: @@ -1249,21 +1161,21 @@ class App: ai_client.clear_comms_log() self._tool_log.clear() self._comms_log.clear() - self.ai_status = "session reset" + self.ai_status = "session reset" self.ai_response = "" if imgui.menu_item("Generate MD Only", "", False)[0]: try: - md, path, *_ = self._do_generate() - self.last_md = md + md, path, *_ = self._do_generate() + self.last_md = md self.last_md_path = path - self.ai_status = f"md written: {path.name}" + self.ai_status = f"md written: {path.name}" except Exception as e: self.ai_status = f"error: {e}" with imscope.menu("Layout") as (active): if active: if imgui.menu_item("Save Current...", "", False)[0]: self._show_save_workspace_profile_modal = True - self._new_workspace_profile_name = "" + self._new_workspace_profile_name = "" imgui.separator() for profile_id, profile in self.workspace_profiles.items(): if imgui.menu_item(profile.name, "", False)[0]: @@ -1306,33 +1218,22 @@ class App: win32gui.SendMessage(hwnd, win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0) imgui.push_style_color(imgui.Col_.button, imgui.ImVec4(0, 0, 0, 0)) - - try: - is_max = win32gui.GetWindowPlacement(hwnd)[1] == win32con.SW_SHOWMAXIMIZED - except Exception: - is_max = False - + #Note(Ed): Thirdparty Exception + 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 imgui.set_cursor_pos((right_x, 0)) - if imgui.button("_", (btn_w, bar_h)): - win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE) - + if imgui.button("_", (btn_w, bar_h)): win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE) imgui.set_cursor_pos((right_x + btn_w, 0)) - if imgui.button("[=]" if is_max else "[]", (btn_w, bar_h)): - win32gui.ShowWindow(hwnd, win32con.SW_RESTORE if is_max else win32con.SW_MAXIMIZE) - + if imgui.button("[=]" if is_max else "[]", (btn_w, bar_h)): win32gui.ShowWindow(hwnd, win32con.SW_RESTORE if is_max else win32con.SW_MAXIMIZE) imgui.set_cursor_pos((right_x + btn_w * 2, 0)) imgui.push_style_color(imgui.Col_.button_hovered, theme.get_color("status_error")) - if imgui.button("X", (btn_w, bar_h)): - win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0) + if imgui.button("X", (btn_w, bar_h)): win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0) imgui.pop_style_color() imgui.pop_style_color() def _handle_history_logic(self) -> None: - """ - - Logic for capturing UI state for undo/redo. - """ + """Logic for capturing UI state for undo/redo.""" if self._is_applying_snapshot: return @@ -2495,12 +2396,10 @@ def render_paths_panel(app: App) -> None: #region: AI Settings def render_ai_settings_hub(app: App) -> None: - """ - Groups and renders all AI-related configuration panels in a unified hub sidebar. + """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 Shape: `[I:persona_selector] -> [B:provider_header] -> [B:system_prompts_header] -> [B:rag_header] -> [I:agent_tools]` ASCII Layout Map: +---------------------------------------------------------+ @@ -2669,6 +2568,7 @@ def render_agent_tools_panel(app: App) -> None: | Bias Profile: [None v] | +---------------------------------------------------------+ """ + caps = app._get_active_capabilities() if not caps.tool_calling: if imgui.collapsing_header("Active Tool Presets & Biases", imgui.TreeNodeFlags_.default_open): imgui.text_disabled(f"(tools not supported by {app.current_provider}/{app.current_model})")