Private
Public Access
0
0

curation pass on gui_2.py

This commit is contained in:
2026-06-12 23:38:31 -04:00
parent d4fbcb16d9
commit 26b1ec77a4
+136 -236
View File
@@ -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})")