Private
Public Access
curation pass on gui_2.py
This commit is contained in:
+136
-236
@@ -22,7 +22,7 @@ _thirdparty = os.path.join(_project_root, "thirdparty")
|
|||||||
if _thirdparty not in sys.path:
|
if _thirdparty not in sys.path:
|
||||||
sys.path.insert(0, _thirdparty)
|
sys.path.insert(0, _thirdparty)
|
||||||
|
|
||||||
from contextlib import ExitStack, nullcontext
|
from contextlib import ExitStack, nullcontext
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Any
|
from typing import Optional, Any
|
||||||
from imgui_bundle import imgui, hello_imgui, immapp, imgui_node_editor as ed, imgui_color_text_edit as ced
|
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 Any as _Any
|
||||||
from typing import Optional as _Optional
|
from typing import Optional as _Optional
|
||||||
|
|
||||||
|
#TODO(Ed): Remove Excpetion based errors
|
||||||
class _LazyModule:
|
class _LazyModule:
|
||||||
"""Lazy proxy that defers an import until first attribute access or call.
|
"""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 askdirectory(self, *args: _Any, **kwargs: _Any) -> str: return ""
|
||||||
def asksaveasfilename(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):
|
# 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
|
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.diff_viewer import apply_patch_to_file
|
||||||
from src import ai_client
|
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
|
shelled out to PowerShell + WMI (Get-CimInstance Win32_VideoController), which
|
||||||
cost ~350ms on every startup and blocked the first frame.
|
cost ~350ms on every startup and blocked the first frame.
|
||||||
"""
|
"""
|
||||||
|
#Note(Ed): Exception(Thirdparty)
|
||||||
try:
|
try:
|
||||||
import ctypes
|
import ctypes
|
||||||
from ctypes import wintypes
|
from ctypes import wintypes
|
||||||
@@ -233,6 +234,7 @@ def _resolve_font_path(font_path: str, assets_dir: Path) -> str:
|
|||||||
p = Path(font_path)
|
p = Path(font_path)
|
||||||
if not p.is_absolute():
|
if not p.is_absolute():
|
||||||
return font_path # already relative; hello_imgui searches the assets folder
|
return font_path # already relative; hello_imgui searches the assets folder
|
||||||
|
#Note(Ed): Exception(Thirdparty)
|
||||||
try:
|
try:
|
||||||
if p.is_relative_to(assets_dir):
|
if p.is_relative_to(assets_dir):
|
||||||
return str(p.relative_to(assets_dir)).replace("\\", "/")
|
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]] = []
|
enabled: list[tuple[str, str]] = []
|
||||||
for field_name, label in badged_fields:
|
for field_name, label in badged_fields:
|
||||||
if getattr(caps, field_name, False):
|
if getattr(caps, field_name, False):
|
||||||
enabled.append((field_name, label))
|
enabled.append((field_name, label))
|
||||||
if not enabled: return
|
if not enabled: return
|
||||||
imgui.text("Capabilities")
|
imgui.text("Capabilities")
|
||||||
@@ -297,16 +299,9 @@ class App:
|
|||||||
"""The main ImGui interface orchestrator for Manual Slop."""
|
"""The main ImGui interface orchestrator for Manual Slop."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
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.
|
command palette, workspace manager) and registers app callback handlers.
|
||||||
|
SSDL Shape: `[I:init_controller] -> [I:init_workspace] -> [I:load_profiles]`
|
||||||
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]`
|
|
||||||
"""
|
"""
|
||||||
#region: --- Core Dependencies & State ---
|
#region: --- Core Dependencies & State ---
|
||||||
from src.startup_profiler import startup_profiler
|
from src.startup_profiler import startup_profiler
|
||||||
@@ -505,22 +500,22 @@ class App:
|
|||||||
self.node_editor_ctx = ed.create_editor(self.node_editor_config)
|
self.node_editor_ctx = ed.create_editor(self.node_editor_config)
|
||||||
|
|
||||||
# --- Context & AST State ---
|
# --- Context & AST State ---
|
||||||
self.ui_selected_ticket_id: Optional[str] = None
|
self.ui_selected_ticket_id: Optional[str] = None
|
||||||
self.ui_selected_tickets: set[str] = set()
|
self.ui_selected_tickets: set[str] = set()
|
||||||
self.ui_selected_context_files: set[str] = set()
|
self.ui_selected_context_files: set[str] = set()
|
||||||
self.ui_new_ticket_priority: str = 'medium'
|
self.ui_new_ticket_priority: str = 'medium'
|
||||||
self._autofocus_response_tab = False
|
self._autofocus_response_tab = False
|
||||||
self._last_selected_context_index = -1
|
self._last_selected_context_index = -1
|
||||||
self.ui_inspecting_ast_file = None
|
self.ui_inspecting_ast_file = None
|
||||||
self._show_ast_inspector = False
|
self._show_ast_inspector = False
|
||||||
self._cached_ast_nodes = []
|
self._cached_ast_nodes = []
|
||||||
self._cached_ast_file_path = ''
|
self._cached_ast_file_path = ''
|
||||||
self._cached_ast_file_lines = []
|
self._cached_ast_file_lines = []
|
||||||
self.ui_editing_slices_file = None
|
self.ui_editing_slices_file = None
|
||||||
self._slice_sel_start = -1
|
self._slice_sel_start = -1
|
||||||
self._slice_sel_end = -1
|
self._slice_sel_end = -1
|
||||||
self.context_files = []
|
self.context_files = []
|
||||||
self.ui_synthesis_prompt: str = ""
|
self.ui_synthesis_prompt: str = ""
|
||||||
self.ui_synthesis_selected_takes: dict[str, bool] = {}
|
self.ui_synthesis_selected_takes: dict[str, bool] = {}
|
||||||
|
|
||||||
# --- Rendering & Theme State ---
|
# --- Rendering & Theme State ---
|
||||||
@@ -564,18 +559,17 @@ class App:
|
|||||||
# is safe. The render_warmup_status_indicator() function reads
|
# is safe. The render_warmup_status_indicator() function reads
|
||||||
# the timestamp to show a brief "ready" tag for 3 seconds.
|
# the timestamp to show a brief "ready" tag for 3 seconds.
|
||||||
if hasattr(self.controller, "on_warmup_complete"):
|
if hasattr(self.controller, "on_warmup_complete"):
|
||||||
|
#Note(Ed): Exception(Thirdparty)
|
||||||
try:
|
try:
|
||||||
self.controller.on_warmup_complete(lambda status: _on_warmup_complete_callback(self, status))
|
self.controller.on_warmup_complete(lambda status: _on_warmup_complete_callback(self, status))
|
||||||
except Exception: pass
|
except Exception: pass
|
||||||
self._diag_layout_state()
|
self._diag_layout_state()
|
||||||
|
|
||||||
def _diag_layout_state(self) -> None:
|
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
|
on-disk manualslop_layout.ini references window names that no longer
|
||||||
exist in the current code. Helps users and test operators detect
|
exist in the current code. Helps users and test operators detect
|
||||||
stale layout state at a glance instead of debugging missing panels.
|
stale layout state at a glance instead of debugging missing panels.
|
||||||
[C: src/gui_2.py:App._post_init]
|
|
||||||
"""
|
"""
|
||||||
import os as _os
|
import os as _os
|
||||||
visible_by_default = [w for w, v in self.show_windows.items() if v]
|
visible_by_default = [w for w, v in self.show_windows.items() if v]
|
||||||
@@ -588,6 +582,7 @@ class App:
|
|||||||
return
|
return
|
||||||
ini_size = _os.path.getsize(ini_path)
|
ini_size = _os.path.getsize(ini_path)
|
||||||
sys.stderr.write(f"[GUI] layout file: {ini_path} ({ini_size} bytes)\n")
|
sys.stderr.write(f"[GUI] layout file: {ini_path} ({ini_size} bytes)\n")
|
||||||
|
#Note(Ed): Exception(Thirdparty)
|
||||||
try:
|
try:
|
||||||
with open(ini_path, encoding="utf-8") as _f:
|
with open(ini_path, encoding="utf-8") as _f:
|
||||||
_ini_text = _f.read()
|
_ini_text = _f.read()
|
||||||
@@ -611,14 +606,9 @@ class App:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def run(self) -> None:
|
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.
|
Loads system themes, default styling metrics, fonts, and sets up window docking layouts.
|
||||||
|
SSDL: `[I:hello_imgui] -> o-> [I:main_loop]`
|
||||||
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]
|
|
||||||
"""
|
"""
|
||||||
if "--headless" in sys.argv:
|
if "--headless" in sys.argv:
|
||||||
print("Headless mode active")
|
print("Headless mode active")
|
||||||
@@ -642,15 +632,15 @@ class App:
|
|||||||
# (removed stale _t-based print; the phase() above already logs RunnerParams_init)
|
# (removed stale _t-based print; the phase() above already logs RunnerParams_init)
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
self.runner_params.app_window_params.borderless = True
|
self.runner_params.app_window_params.borderless = True
|
||||||
self.runner_params.app_window_params.borderless_closable = False
|
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_movable = False
|
||||||
self.runner_params.app_window_params.borderless_resizable = True
|
self.runner_params.app_window_params.borderless_resizable = True
|
||||||
|
|
||||||
self.runner_params.app_window_params.window_geometry.size = (1680, 1200)
|
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.enable_viewports = getattr(self, "ui_multi_viewport", False)
|
||||||
self.runner_params.imgui_window_params.remember_theme = True
|
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.tweaked_theme = theme.get_tweaked_theme()
|
||||||
self.runner_params.imgui_window_params.default_imgui_window_type = hello_imgui.DefaultImGuiWindowType.provide_full_screen_dock_space
|
self.runner_params.imgui_window_params.default_imgui_window_type = hello_imgui.DefaultImGuiWindowType.provide_full_screen_dock_space
|
||||||
|
|
||||||
# Enforce DPI Awareness and User Scale
|
# Enforce DPI Awareness and User Scale
|
||||||
@@ -667,25 +657,26 @@ class App:
|
|||||||
|
|
||||||
# Enable idling with monitor refresh rate to effectively cap FPS
|
# Enable idling with monitor refresh rate to effectively cap FPS
|
||||||
self.runner_params.fps_idling.enable_idling = True
|
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.imgui_window_params.show_menu_view_themes = True
|
||||||
self.runner_params.ini_folder_type = hello_imgui.IniFolderType.current_folder
|
self.runner_params.ini_folder_type = hello_imgui.IniFolderType.current_folder
|
||||||
self.runner_params.ini_filename = "manualslop_layout.ini"
|
self.runner_params.ini_filename = "manualslop_layout.ini"
|
||||||
def _profiled_setup_style() -> None:
|
def _profiled_setup_style() -> None:
|
||||||
with startup_profiler.phase("setup_imgui_style"):
|
with startup_profiler.phase("setup_imgui_style"):
|
||||||
theme.apply_current()
|
theme.apply_current()
|
||||||
def _profiled_post_init() -> None:
|
def _profiled_post_init() -> None:
|
||||||
with startup_profiler.phase("post_init"):
|
with startup_profiler.phase("post_init"):
|
||||||
self._post_init()
|
self._post_init()
|
||||||
self.runner_params.callbacks.show_gui = self._gui_func
|
self.runner_params.callbacks.show_gui = self._gui_func
|
||||||
self.runner_params.callbacks.show_menus = self._show_menus
|
self.runner_params.callbacks.show_menus = self._show_menus
|
||||||
self.runner_params.callbacks.load_additional_fonts = self._load_fonts
|
self.runner_params.callbacks.load_additional_fonts = self._load_fonts
|
||||||
self.runner_params.callbacks.setup_imgui_style = _profiled_setup_style
|
self.runner_params.callbacks.setup_imgui_style = _profiled_setup_style
|
||||||
self.runner_params.callbacks.post_init = _profiled_post_init
|
self.runner_params.callbacks.post_init = _profiled_post_init
|
||||||
self._fetch_models(self.current_provider)
|
self._fetch_models(self.current_provider)
|
||||||
md_options = markdown_helper.get_renderer().options
|
md_options = markdown_helper.get_renderer().options
|
||||||
|
#Note(Ed): Exception(Thirdparty)
|
||||||
try:
|
try:
|
||||||
immapp.run(self.runner_params, add_ons_params=immapp.AddOnsParams(with_markdown_options=md_options))
|
immapp.run(self.runner_params, add_ons_params=immapp.AddOnsParams(with_markdown_options=md_options))
|
||||||
except RuntimeError as _immapp_exc:
|
except RuntimeError as _immapp_exc:
|
||||||
@@ -703,8 +694,8 @@ class App:
|
|||||||
self.controller._last_imgui_assert = traceback.format_exc()
|
self.controller._last_imgui_assert = traceback.format_exc()
|
||||||
print(
|
print(
|
||||||
f"[GUI-DEGRADED] immapp.run raised: {_immapp_exc}",
|
f"[GUI-DEGRADED] immapp.run raised: {_immapp_exc}",
|
||||||
file=sys.stderr,
|
file = sys.stderr,
|
||||||
flush=True,
|
flush = True,
|
||||||
)
|
)
|
||||||
print(self.controller._last_imgui_assert if hasattr(self, "controller") and self.controller else "",
|
print(self.controller._last_imgui_assert if hasattr(self, "controller") and self.controller else "",
|
||||||
file=sys.stderr, flush=True)
|
file=sys.stderr, flush=True)
|
||||||
@@ -722,7 +713,7 @@ class App:
|
|||||||
hello_imgui.set_assets_folder(str(assets_dir.absolute()))
|
hello_imgui.set_assets_folder(str(assets_dir.absolute()))
|
||||||
|
|
||||||
# Improved font rendering with oversampling
|
# Improved font rendering with oversampling
|
||||||
config = imgui.ImFontConfig()
|
config = imgui.ImFontConfig()
|
||||||
config.oversample_h = 3
|
config.oversample_h = 3
|
||||||
config.oversample_v = 3
|
config.oversample_v = 3
|
||||||
|
|
||||||
@@ -730,6 +721,7 @@ class App:
|
|||||||
|
|
||||||
if font_path:
|
if font_path:
|
||||||
font_path = _resolve_font_path(font_path, assets_dir)
|
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
|
# Just try loading it directly; hello_imgui will look in the assets folder
|
||||||
try:
|
try:
|
||||||
with startup_profiler.phase("load_fonts.main_with_fontawesome"):
|
with startup_profiler.phase("load_fonts.main_with_fontawesome"):
|
||||||
@@ -739,10 +731,11 @@ class App:
|
|||||||
self.main_font = None
|
self.main_font = None
|
||||||
else:
|
else:
|
||||||
self.main_font = None
|
self.main_font = None
|
||||||
|
|
||||||
|
#Note(Ed): Exception(Thirdparty)
|
||||||
try:
|
try:
|
||||||
with startup_profiler.phase("load_fonts.mono"):
|
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)
|
self.mono_font = hello_imgui.load_font("fonts/MapleMono-Regular.ttf", font_size, params)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to load mono font: {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."""
|
"""UI-level wrapper for approving a pending MMA sub-agent spawn."""
|
||||||
self._handle_mma_respond(approved=True)
|
self._handle_mma_respond(approved=True)
|
||||||
|
|
||||||
|
#TODO(Ed): Remove Exception based errors.
|
||||||
def __getattr__(self, name: str) -> Any:
|
def __getattr__(self, name: str) -> Any:
|
||||||
if name == 'controller':
|
if name == 'controller':
|
||||||
raise AttributeError(name)
|
raise AttributeError(name)
|
||||||
@@ -772,7 +766,7 @@ class App:
|
|||||||
def _handle_generate_send(self) -> None:
|
def _handle_generate_send(self) -> None:
|
||||||
if not self.ui_selected_context_files and not getattr(self, "_pending_proceed_generate", False):
|
if not self.ui_selected_context_files and not getattr(self, "_pending_proceed_generate", False):
|
||||||
self._pending_generation_action = 'generate'
|
self._pending_generation_action = 'generate'
|
||||||
self.show_empty_context_modal = True
|
self.show_empty_context_modal = True
|
||||||
else:
|
else:
|
||||||
self._pending_proceed_generate = False
|
self._pending_proceed_generate = False
|
||||||
self.controller._handle_generate_send()
|
self.controller._handle_generate_send()
|
||||||
@@ -780,13 +774,13 @@ class App:
|
|||||||
def _handle_md_only(self) -> None:
|
def _handle_md_only(self) -> None:
|
||||||
if not self.ui_selected_context_files and not getattr(self, "_pending_proceed_md_only", False):
|
if not self.ui_selected_context_files and not getattr(self, "_pending_proceed_md_only", False):
|
||||||
self._pending_generation_action = 'md_only'
|
self._pending_generation_action = 'md_only'
|
||||||
self.show_empty_context_modal = True
|
self.show_empty_context_modal = True
|
||||||
else:
|
else:
|
||||||
self._pending_proceed_md_only = False
|
self._pending_proceed_md_only = False
|
||||||
self.controller._handle_md_only()
|
self.controller._handle_md_only()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_provider(self) -> str:
|
def current_provider(self) -> str:
|
||||||
return self.controller.current_provider
|
return self.controller.current_provider
|
||||||
|
|
||||||
@current_provider.setter
|
@current_provider.setter
|
||||||
@@ -801,8 +795,10 @@ class App:
|
|||||||
def current_model(self, value: str) -> None:
|
def current_model(self, value: str) -> None:
|
||||||
self.controller.current_model = value
|
self.controller.current_model = value
|
||||||
|
|
||||||
|
#TODO(Ed): Remove Exception based errors.
|
||||||
def _get_active_capabilities(self) -> "VendorCapabilities":
|
def _get_active_capabilities(self) -> "VendorCapabilities":
|
||||||
from src.vendor_capabilities import VendorCapabilities, get_capabilities
|
from src.vendor_capabilities import VendorCapabilities, get_capabilities
|
||||||
|
#TODO(Ed): Remove Exception based errors.
|
||||||
try:
|
try:
|
||||||
caps = get_capabilities(self.current_provider, self.current_model)
|
caps = get_capabilities(self.current_provider, self.current_model)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -824,27 +820,21 @@ class App:
|
|||||||
@property
|
@property
|
||||||
def app_debug_info(self) -> dict:
|
def app_debug_info(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"context_files": [f.path for f in self.context_files],
|
"context_files": [f.path for f in self.context_files],
|
||||||
"missing_files": self.missing_files,
|
"missing_files": self.missing_files,
|
||||||
"screenshots": self.screenshots,
|
"screenshots": self.screenshots,
|
||||||
"ui_new_context_preset_name": self.ui_new_context_preset_name,
|
"ui_new_context_preset_name": self.ui_new_context_preset_name,
|
||||||
"target_context_preset_name": self.target_context_preset_name,
|
"target_context_preset_name": self.target_context_preset_name,
|
||||||
"show_missing_files_modal": self.show_missing_files_modal,
|
"show_missing_files_modal": self.show_missing_files_modal,
|
||||||
"active_project_root": str(self.controller.active_project_root),
|
"active_project_root": str(self.controller.active_project_root),
|
||||||
"project_keys": list(self.controller.project.keys()),
|
"project_keys": list(self.controller.project.keys()),
|
||||||
"presets": list(self.controller.project.get('context_presets', {}).keys())
|
"presets": list(self.controller.project.get('context_presets', {}).keys())
|
||||||
}
|
}
|
||||||
|
|
||||||
def _take_snapshot(self) -> history.UISnapshot:
|
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.
|
discussions, and files list, returning a UISnapshot for history management.
|
||||||
|
SSDL: `[Q:ui_state] -> [I:copy] -> [T:snapshot]`
|
||||||
State Mutations:
|
|
||||||
None (read-only state capture).
|
|
||||||
|
|
||||||
SSDL Shape:
|
|
||||||
`[Q:ui_state] -> [I:copy] -> [T:snapshot]`
|
|
||||||
"""
|
"""
|
||||||
from src import history
|
from src import history
|
||||||
import copy
|
import copy
|
||||||
@@ -865,16 +855,9 @@ class App:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _apply_snapshot(self, snapshot: history.UISnapshot) -> None:
|
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.
|
Restores input fields, parameters, discussions, screenshots, and context files.
|
||||||
|
SSDL Shape: `[I:lock_flag] -> [S:ui_state] -> [I:unlock]`
|
||||||
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]`
|
|
||||||
"""
|
"""
|
||||||
self._is_applying_snapshot = True
|
self._is_applying_snapshot = True
|
||||||
try:
|
try:
|
||||||
@@ -893,42 +876,31 @@ class App:
|
|||||||
from src import models
|
from src import models
|
||||||
self.files = []
|
self.files = []
|
||||||
for f in snapshot.files:
|
for f in snapshot.files:
|
||||||
if isinstance(f, dict):
|
if isinstance(f, dict): self.files.append(models.FileItem.from_dict(f))
|
||||||
self.files.append(models.FileItem.from_dict(f))
|
else: self.files.append(models.FileItem(path=str(f)))
|
||||||
else:
|
|
||||||
self.files.append(models.FileItem(path=str(f)))
|
|
||||||
|
|
||||||
self.context_files = []
|
self.context_files = []
|
||||||
for f in snapshot.context_files:
|
for f in snapshot.context_files:
|
||||||
if isinstance(f, dict):
|
if isinstance(f, dict): self.context_files.append(models.FileItem.from_dict(f))
|
||||||
self.context_files.append(models.FileItem.from_dict(f))
|
else: self.context_files.append(models.FileItem(path=str(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
|
self._last_ui_snapshot = snapshot # Update last snapshot to avoid immediate re-push
|
||||||
finally:
|
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:
|
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.
|
ImGui INI configurations into a WorkspaceProfile object.
|
||||||
|
SSDL Shape: `[Q:ui_states] -> [B:ini_ready] -> [T:profile]`
|
||||||
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]`
|
|
||||||
"""
|
"""
|
||||||
if not getattr(self, "_ini_capture_ready", False):
|
if not getattr(self, "_ini_capture_ready", False):
|
||||||
self._ini_capture_ready = True
|
self._ini_capture_ready = True
|
||||||
ini = ""
|
ini = ""
|
||||||
else:
|
else:
|
||||||
try:
|
#Note(Ed): Thirdparty Exception
|
||||||
ini = str(imgui.save_ini_settings_to_memory() or "")
|
try: ini = str(imgui.save_ini_settings_to_memory() or "")
|
||||||
except Exception:
|
except Exception: ini = ""
|
||||||
ini = ""
|
|
||||||
panel_states = {
|
panel_states = {
|
||||||
"ui_separate_context_preview": getattr(self, "ui_separate_context_preview", False),
|
"ui_separate_context_preview": getattr(self, "ui_separate_context_preview", False),
|
||||||
"ui_separate_message_panel": getattr(self, "ui_separate_message_panel", 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),
|
"ui_discussion_split_h": getattr(self, "ui_discussion_split_h", 300.0),
|
||||||
}
|
}
|
||||||
return models.WorkspaceProfile(
|
return models.WorkspaceProfile(
|
||||||
name=name,
|
name = name,
|
||||||
ini_content=ini,
|
ini_content = ini,
|
||||||
show_windows=copy.deepcopy(self.show_windows),
|
show_windows = copy.deepcopy(self.show_windows),
|
||||||
panel_states=panel_states
|
panel_states = panel_states
|
||||||
)
|
)
|
||||||
|
|
||||||
def _apply_workspace_profile(self, profile: models.WorkspaceProfile):
|
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.
|
from a saved WorkspaceProfile.
|
||||||
|
SSDL Shape: `[I:load_ini] -> [S:ui_states]`
|
||||||
State Mutations:
|
|
||||||
Modifies window visibility and panel configuration state variables.
|
|
||||||
|
|
||||||
SSDL Shape:
|
|
||||||
`[I:load_ini] -> [S:ui_states]`
|
|
||||||
"""
|
"""
|
||||||
imgui.load_ini_settings_from_memory(profile.ini_content)
|
imgui.load_ini_settings_from_memory(profile.ini_content)
|
||||||
self.show_windows.update(profile.show_windows)
|
self.show_windows.update(profile.show_windows)
|
||||||
for k, v in profile.panel_states.items():
|
for k, v in profile.panel_states.items():
|
||||||
if hasattr(self, k):
|
if hasattr(self, k): setattr(self, k, v)
|
||||||
setattr(self, k, v)
|
|
||||||
|
|
||||||
def _handle_undo(self) -> None:
|
def _handle_undo(self) -> None:
|
||||||
"""
|
"""Reverts the application UI state to the previous snapshot in the history stack.
|
||||||
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()
|
|
||||||
|
|
||||||
DAG Render Context:
|
DAG Render Context:
|
||||||
Called by: _gui_func() (via hotkey Ctrl+Z) or undo button click.
|
Called by: _gui_func() (via hotkey Ctrl+Z) or undo button click.
|
||||||
Calls: _take_snapshot(), _apply_snapshot(), HistoryManager.undo()
|
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.write(f"[DEBUG History] _handle_undo called. can_undo={self.history.can_undo}\n"); sys.stderr.flush()
|
||||||
sys.stderr.flush()
|
if not self.history.can_undo: return
|
||||||
if not self.history.can_undo:
|
|
||||||
return
|
|
||||||
current = self._take_snapshot()
|
current = self._take_snapshot()
|
||||||
entry = self.history.undo(current, "Undo Action")
|
entry = self.history.undo(current, "Undo Action")
|
||||||
if entry:
|
if entry:
|
||||||
sys.stderr.write(f"[DEBUG History] Undoing to: {entry.description}\n")
|
sys.stderr.write(f"[DEBUG History] Undoing to: {entry.description}\n"); sys.stderr.flush()
|
||||||
sys.stderr.flush()
|
|
||||||
self._apply_snapshot(entry.state)
|
self._apply_snapshot(entry.state)
|
||||||
|
|
||||||
def _handle_jump_to_history(self, index: int) -> None:
|
def _handle_jump_to_history(self, index: int) -> None:
|
||||||
sys.stderr.write(f"[DEBUG History] Jumping to index {index}\n")
|
sys.stderr.write(f"[DEBUG History] Jumping to index {index}\n"); sys.stderr.flush()
|
||||||
sys.stderr.flush()
|
|
||||||
current = self._take_snapshot()
|
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:
|
if entry:
|
||||||
self._apply_snapshot(entry.state)
|
self._apply_snapshot(entry.state)
|
||||||
|
|
||||||
def _handle_redo(self) -> None:
|
def _handle_redo(self) -> None:
|
||||||
"""
|
"""Re-applies the next snapshot in the history stack (forward navigation).
|
||||||
Re-applies the next snapshot in the history stack (forward navigation).
|
SSDL Shape: `[I:snapshot] -> [B:history] => [I:state]`
|
||||||
|
|
||||||
State Mutations:
|
|
||||||
self.history (mutated to record index changes)
|
|
||||||
Modifies active UI variables via _apply_snapshot()
|
|
||||||
|
|
||||||
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.write(f"[DEBUG History] _handle_redo called. can_redo={self.history.can_redo}\n")
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
@@ -1024,17 +969,9 @@ class App:
|
|||||||
self._apply_snapshot(entry.state)
|
self._apply_snapshot(entry.state)
|
||||||
|
|
||||||
def shutdown(self) -> None:
|
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.
|
forces a save of dirty registries/caches, and terminates the active thread pools.
|
||||||
|
SSDL Shape: `[I:save_ini] -> [I:controller_shutdown]`
|
||||||
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]
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if hasattr(self, 'runner_params') and self.runner_params.ini_filename:
|
if hasattr(self, 'runner_params') and self.runner_params.ini_filename:
|
||||||
@@ -1044,29 +981,23 @@ class App:
|
|||||||
self.controller.shutdown()
|
self.controller.shutdown()
|
||||||
|
|
||||||
def load_context_preset(self, name: str) -> None:
|
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)
|
preset = self.controller.load_context_preset(name)
|
||||||
from src import models
|
from src import models
|
||||||
import copy
|
import copy
|
||||||
self.context_files = []
|
self.context_files = []
|
||||||
for f in preset.files:
|
for f in preset.files:
|
||||||
fi = models.FileItem(path=f.path, view_mode=f.view_mode)
|
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.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_mask = copy.deepcopy(f.ast_mask) if hasattr(f, 'ast_mask') else {}
|
||||||
fi.ast_signatures = getattr(f, 'ast_signatures', False)
|
fi.ast_signatures = getattr(f, 'ast_signatures', False)
|
||||||
fi.ast_definitions = getattr(f, 'ast_definitions', False)
|
fi.ast_definitions = getattr(f, 'ast_definitions', False)
|
||||||
self.context_files.append(fi)
|
self.context_files.append(fi)
|
||||||
self.screenshots = list(preset.screenshots)
|
self.screenshots = list(preset.screenshots)
|
||||||
self.ui_file_paths = [f.path for f in preset.files]
|
self.ui_file_paths = [f.path for f in preset.files]
|
||||||
self.ui_screenshot_paths = list(preset.screenshots)
|
self.ui_screenshot_paths = list(preset.screenshots)
|
||||||
self._update_context_file_stats()
|
self._update_context_file_stats()
|
||||||
|
|
||||||
def delete_context_preset(self, name: str) -> None:
|
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)
|
self.controller.delete_context_preset(name)
|
||||||
if getattr(self, "ui_active_context_preset", "") == name:
|
if getattr(self, "ui_active_context_preset", "") == name:
|
||||||
self.ui_active_context_preset = ""
|
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."""
|
since the keyboard shortcut (Ctrl+Shift+P) cannot be simulated via the hook API."""
|
||||||
self.show_command_palette = not self.show_command_palette
|
self.show_command_palette = not self.show_command_palette
|
||||||
if self.show_command_palette:
|
if self.show_command_palette:
|
||||||
if hasattr(self, '_command_palette_query'):
|
if hasattr(self, '_command_palette_query'): self._command_palette_query = ""
|
||||||
self._command_palette_query = ""
|
if hasattr(self, '_command_palette_selected'): self._command_palette_selected = 0
|
||||||
if hasattr(self, '_command_palette_selected'):
|
if hasattr(self, '_command_palette_input_focused'): self._command_palette_input_focused = False
|
||||||
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:
|
def _test_callback_func_write_to_file(self, data: str) -> None:
|
||||||
"""A dummy function that a custom_callback would execute for testing."""
|
"""A dummy function that a custom_callback would execute for testing."""
|
||||||
@@ -1118,17 +1046,11 @@ class App:
|
|||||||
f.write(data)
|
f.write(data)
|
||||||
|
|
||||||
def _gui_func(self) -> None:
|
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,
|
Dispatches keyboard shortcuts, renders the background shader, custom title bar,
|
||||||
main dockspace, and handles popups/modals.
|
main dockspace, and handles popups/modals.
|
||||||
|
|
||||||
State Mutations:
|
SSDL Shape: `o-> [I:hotkeys] -> [I:title_bar] -> [I:main_interface] -> [I:modals]`
|
||||||
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]`
|
|
||||||
|
|
||||||
ASCII Layout Map:
|
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.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()
|
sys.stderr.flush()
|
||||||
except Exception: pass
|
except Exception: pass
|
||||||
|
|
||||||
# One-shot: kick off the controller's heavy-module warmup on the shared
|
# 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
|
# 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
|
# keeps the ~2s of SDK C-extension imports from holding the GIL during
|
||||||
@@ -1168,33 +1090,29 @@ class App:
|
|||||||
self._preload_started = True
|
self._preload_started = True
|
||||||
else:
|
else:
|
||||||
self._first_frame_painted = True
|
self._first_frame_painted = True
|
||||||
|
|
||||||
io = imgui.get_io()
|
io = imgui.get_io()
|
||||||
if io.key_ctrl and io.key_alt and imgui.is_key_down(imgui.Key.r):
|
if io.key_ctrl and io.key_alt and imgui.is_key_down(imgui.Key.r): self._trigger_hot_reload()
|
||||||
self._trigger_hot_reload()
|
|
||||||
if (io.key_ctrl and io.key_shift
|
if (io.key_ctrl and io.key_shift
|
||||||
and not io.key_alt and not io.key_super
|
and not io.key_alt and not io.key_super
|
||||||
and imgui.is_key_pressed(imgui.Key.p)):
|
and imgui.is_key_pressed(imgui.Key.p)):
|
||||||
self.show_command_palette = not self.show_command_palette
|
self.show_command_palette = not self.show_command_palette
|
||||||
if self.show_command_palette:
|
if self.show_command_palette:
|
||||||
if hasattr(self, '_command_palette_query'):
|
if hasattr(self, '_command_palette_query'): self._command_palette_query = ""
|
||||||
self._command_palette_query = ""
|
if hasattr(self, '_command_palette_selected'): self._command_palette_selected = 0
|
||||||
if hasattr(self, '_command_palette_selected'):
|
|
||||||
self._command_palette_selected = 0
|
|
||||||
|
|
||||||
render_custom_title_bar(self)
|
render_custom_title_bar(self)
|
||||||
render_shader_live_editor(self)
|
render_shader_live_editor(self)
|
||||||
render_history_window(self)
|
render_history_window(self)
|
||||||
pushed_prior_tint = False
|
pushed_prior_tint = False
|
||||||
|
|
||||||
# Render background shader
|
# Render background shader
|
||||||
bg = bg_shader.get_bg()
|
bg = bg_shader.get_bg()
|
||||||
ws = imgui.get_io().display_size
|
ws = imgui.get_io().display_size
|
||||||
if bg.enabled:
|
if bg.enabled: bg.render(ws.x, ws.y)
|
||||||
bg.render(ws.x, ws.y)
|
|
||||||
|
|
||||||
theme.render_post_fx(ws.x, ws.y, self.ai_status, self.ui_crt_filter)
|
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")
|
if self.perf_profiling_enabled: self.perf_monitor.start_component("_gui_func")
|
||||||
try:
|
try:
|
||||||
if self.is_viewing_prior_session:
|
if self.is_viewing_prior_session:
|
||||||
@@ -1205,11 +1123,9 @@ class App:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
sys.stderr.write(f"ERROR in _gui_func: {e}\n")
|
sys.stderr.write(f"ERROR in _gui_func: {e}\n")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
self._handle_history_logic()
|
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
|
return
|
||||||
|
|
||||||
def _render_window_if_open(self, name: str, render_func: Callable[[], None], flag_condition: bool = True) -> None:
|
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()
|
if exp: render_func()
|
||||||
|
|
||||||
def _show_menus(self) -> None:
|
def _show_menus(self) -> None:
|
||||||
"""
|
|
||||||
[C: tests/test_gui_window_controls.py:test_gui_window_controls_minimize_maximize_close]
|
|
||||||
"""
|
|
||||||
global win32gui, win32con
|
global win32gui, win32con
|
||||||
if win32gui is None:
|
if win32gui is None:
|
||||||
import win32con
|
import win32con
|
||||||
@@ -1231,11 +1144,10 @@ class App:
|
|||||||
win32gui = win32gui
|
win32gui = win32gui
|
||||||
|
|
||||||
with imscope.menu("manual slop") as (active):
|
with imscope.menu("manual slop") as (active):
|
||||||
if active and imgui.menu_item("Quit", "Ctrl+Q", False)[0]:
|
if active and imgui.menu_item("Quit", "Ctrl+Q", False)[0]: self.runner_params.app_shall_exit = True
|
||||||
self.runner_params.app_shall_exit = True
|
|
||||||
with imscope.menu("Windows") as (active):
|
with imscope.menu("Windows") as (active):
|
||||||
if (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])
|
_, self.show_windows[w] = imgui.menu_item(w, "", self.show_windows[w])
|
||||||
with imscope.menu("Project") as (active):
|
with imscope.menu("Project") as (active):
|
||||||
if active:
|
if active:
|
||||||
@@ -1249,21 +1161,21 @@ class App:
|
|||||||
ai_client.clear_comms_log()
|
ai_client.clear_comms_log()
|
||||||
self._tool_log.clear()
|
self._tool_log.clear()
|
||||||
self._comms_log.clear()
|
self._comms_log.clear()
|
||||||
self.ai_status = "session reset"
|
self.ai_status = "session reset"
|
||||||
self.ai_response = ""
|
self.ai_response = ""
|
||||||
if imgui.menu_item("Generate MD Only", "", False)[0]:
|
if imgui.menu_item("Generate MD Only", "", False)[0]:
|
||||||
try:
|
try:
|
||||||
md, path, *_ = self._do_generate()
|
md, path, *_ = self._do_generate()
|
||||||
self.last_md = md
|
self.last_md = md
|
||||||
self.last_md_path = path
|
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:
|
except Exception as e:
|
||||||
self.ai_status = f"error: {e}"
|
self.ai_status = f"error: {e}"
|
||||||
with imscope.menu("Layout") as (active):
|
with imscope.menu("Layout") as (active):
|
||||||
if active:
|
if active:
|
||||||
if imgui.menu_item("Save Current...", "", False)[0]:
|
if imgui.menu_item("Save Current...", "", False)[0]:
|
||||||
self._show_save_workspace_profile_modal = True
|
self._show_save_workspace_profile_modal = True
|
||||||
self._new_workspace_profile_name = ""
|
self._new_workspace_profile_name = ""
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
for profile_id, profile in self.workspace_profiles.items():
|
for profile_id, profile in self.workspace_profiles.items():
|
||||||
if imgui.menu_item(profile.name, "", False)[0]:
|
if imgui.menu_item(profile.name, "", False)[0]:
|
||||||
@@ -1306,33 +1218,22 @@ class App:
|
|||||||
win32gui.SendMessage(hwnd, win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0)
|
win32gui.SendMessage(hwnd, win32con.WM_NCLBUTTONDOWN, win32con.HTCAPTION, 0)
|
||||||
|
|
||||||
imgui.push_style_color(imgui.Col_.button, imgui.ImVec4(0, 0, 0, 0))
|
imgui.push_style_color(imgui.Col_.button, imgui.ImVec4(0, 0, 0, 0))
|
||||||
|
#Note(Ed): Thirdparty Exception
|
||||||
try:
|
try: is_max = win32gui.GetWindowPlacement(hwnd)[1] == win32con.SW_SHOWMAXIMIZED
|
||||||
is_max = win32gui.GetWindowPlacement(hwnd)[1] == win32con.SW_SHOWMAXIMIZED
|
except Exception: is_max = False
|
||||||
except Exception:
|
|
||||||
is_max = False
|
|
||||||
|
|
||||||
# Explicitly set Y to 0 and match button height to bar height for perfect alignment
|
# Explicitly set Y to 0 and match button height to bar height for perfect alignment
|
||||||
imgui.set_cursor_pos((right_x, 0))
|
imgui.set_cursor_pos((right_x, 0))
|
||||||
if imgui.button("_", (btn_w, bar_h)):
|
if imgui.button("_", (btn_w, bar_h)): win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE)
|
||||||
win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE)
|
|
||||||
|
|
||||||
imgui.set_cursor_pos((right_x + btn_w, 0))
|
imgui.set_cursor_pos((right_x + btn_w, 0))
|
||||||
if imgui.button("[=]" if is_max else "[]", (btn_w, bar_h)):
|
if imgui.button("[=]" if is_max else "[]", (btn_w, bar_h)): win32gui.ShowWindow(hwnd, win32con.SW_RESTORE if is_max else win32con.SW_MAXIMIZE)
|
||||||
win32gui.ShowWindow(hwnd, win32con.SW_RESTORE if is_max else win32con.SW_MAXIMIZE)
|
|
||||||
|
|
||||||
imgui.set_cursor_pos((right_x + btn_w * 2, 0))
|
imgui.set_cursor_pos((right_x + btn_w * 2, 0))
|
||||||
imgui.push_style_color(imgui.Col_.button_hovered, theme.get_color("status_error"))
|
imgui.push_style_color(imgui.Col_.button_hovered, theme.get_color("status_error"))
|
||||||
if imgui.button("X", (btn_w, bar_h)):
|
if imgui.button("X", (btn_w, bar_h)): win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
|
||||||
win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
|
|
||||||
imgui.pop_style_color()
|
imgui.pop_style_color()
|
||||||
imgui.pop_style_color()
|
imgui.pop_style_color()
|
||||||
|
|
||||||
def _handle_history_logic(self) -> None:
|
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:
|
if self._is_applying_snapshot:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -2495,12 +2396,10 @@ def render_paths_panel(app: App) -> None:
|
|||||||
#region: AI Settings
|
#region: AI Settings
|
||||||
|
|
||||||
def render_ai_settings_hub(app: App) -> None:
|
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.
|
Includes persona selection, LLM provider settings, system prompts, RAG config, and tools.
|
||||||
|
|
||||||
SSDL Shape:
|
SSDL Shape: `[I:persona_selector] -> [B:provider_header] -> [B:system_prompts_header] -> [B:rag_header] -> [I:agent_tools]`
|
||||||
`[I:persona_selector] -> [B:provider_header] -> [B:system_prompts_header] -> [B:rag_header] -> [I:agent_tools]`
|
|
||||||
|
|
||||||
ASCII Layout Map:
|
ASCII Layout Map:
|
||||||
+---------------------------------------------------------+
|
+---------------------------------------------------------+
|
||||||
@@ -2669,6 +2568,7 @@ def render_agent_tools_panel(app: App) -> None:
|
|||||||
| Bias Profile: [None v] |
|
| Bias Profile: [None v] |
|
||||||
+---------------------------------------------------------+
|
+---------------------------------------------------------+
|
||||||
"""
|
"""
|
||||||
|
caps = app._get_active_capabilities()
|
||||||
if not caps.tool_calling:
|
if not caps.tool_calling:
|
||||||
if imgui.collapsing_header("Active Tool Presets & Biases", imgui.TreeNodeFlags_.default_open):
|
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})")
|
imgui.text_disabled(f"(tools not supported by {app.current_provider}/{app.current_model})")
|
||||||
|
|||||||
Reference in New Issue
Block a user