diff --git a/src/gui_2.py b/src/gui_2.py index 1d01b36..8a52680 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -108,6 +108,16 @@ class App: self.show_windows.setdefault("Diagnostics", False) self.controller.start_services(self) self.controller._predefined_callbacks['_render_text_viewer'] = self._render_text_viewer + self.controller._predefined_callbacks['save_context_preset'] = self.save_context_preset + self.controller._predefined_callbacks['load_context_preset'] = self.load_context_preset + self.controller._predefined_callbacks['set_ui_file_paths'] = lambda p: setattr(self, 'ui_file_paths', p) + self.controller._predefined_callbacks['set_ui_screenshot_paths'] = lambda p: setattr(self, 'ui_screenshot_paths', p) + def simulate_save_preset(name: str): + from src import models + self.files = [models.FileItem(path='test.py')] + self.screenshots = ['test.png'] + self.save_context_preset(name) + self.controller._predefined_callbacks['simulate_save_preset'] = simulate_save_preset self.show_preset_manager_window = False self.show_tool_preset_manager_window = False self.show_persona_editor_window = False @@ -221,8 +231,8 @@ class App: self.ui_tool_filter_category = "All" self.ui_discussion_split_h = 300.0 self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8} - - def _handle_approve_tool(self, user_data=None) -> None: + self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8} + self.ui_new_context_preset_name = "" """UI-level wrapper for approving a pending tool execution ask.""" self._handle_approve_ask() @@ -280,6 +290,46 @@ class App: pass self.controller.shutdown() + def save_context_preset(self, name: str) -> None: + sys.stderr.write(f"[DEBUG] save_context_preset called with: {name}\n") + sys.stderr.flush() + if 'context_presets' not in self.controller.project: + self.controller.project['context_presets'] = {} + self.controller.project['context_presets'][name] = { + 'files': [f.to_dict() if hasattr(f, 'to_dict') else {'path': str(f)} for f in self.files], + 'screenshots': list(self.screenshots) + } + self.controller._save_active_project() + sys.stderr.write(f"[DEBUG] save_context_preset finished. Project keys: {list(self.controller.project.keys())}\n") + sys.stderr.flush() + + def load_context_preset(self, name: str) -> None: + presets = self.controller.project.get('context_presets', {}) + if name in presets: + preset = presets[name] + self.files = [models.FileItem.from_dict(f) if isinstance(f, dict) else models.FileItem(path=str(f)) for f in preset.get('files', [])] + self.screenshots = list(preset.get('screenshots', [])) + + def delete_context_preset(self, name: str) -> None: + if 'context_presets' in self.controller.project: + self.controller.project['context_presets'].pop(name, None) + self.controller._save_active_project() + @property + def ui_file_paths(self) -> list[str]: + return [f.path if hasattr(f, 'path') else str(f) for f in self.files] + + @ui_file_paths.setter + def ui_file_paths(self, paths: list[str]) -> None: + self.files = [models.FileItem(path=p) for p in paths] + + @property + def ui_screenshot_paths(self) -> list[str]: + return self.screenshots + + @ui_screenshot_paths.setter + def ui_screenshot_paths(self, paths: list[str]) -> None: + self.screenshots = paths + def _test_callback_func_write_to_file(self, data: str) -> None: """A dummy function that a custom_callback would execute for testing.""" # Ensure the directory exists if running from a different cwd @@ -587,6 +637,9 @@ class App: if imgui.begin_tab_item('Paths')[0]: self._render_paths_panel() imgui.end_tab_item() + if imgui.begin_tab_item('Context Presets')[0]: + self._render_context_presets_panel() + imgui.end_tab_item() imgui.end_tab_bar() imgui.end() if self.show_windows.get("Files & Media", False): @@ -1698,6 +1751,30 @@ class App: self.ai_status = "paths reset to defaults" if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_paths_panel") + + def _render_context_presets_panel(self) -> None: + imgui.text_colored(C_IN, "Context Presets") + imgui.separator() + changed, new_name = imgui.input_text("Preset Name##new_ctx", self.ui_new_context_preset_name) + if changed: self.ui_new_context_preset_name = new_name + imgui.same_line() + if imgui.button("Save Current"): + if self.ui_new_context_preset_name.strip(): + self.save_context_preset(self.ui_new_context_preset_name.strip()) + + imgui.separator() + presets = self.controller.project.get('context_presets', {}) + for name in sorted(presets.keys()): + preset = presets[name] + n_files = len(preset.get('files', [])) + n_shots = len(preset.get('screenshots', [])) + imgui.text(f"{name} ({n_files} files, {n_shots} shots)") + imgui.same_line() + if imgui.button(f"Load##{name}"): + self.load_context_preset(name) + imgui.same_line() + if imgui.button(f"Delete##{name}"): + self.delete_context_preset(name) def _render_track_proposal_modal(self) -> None: if self._show_track_proposal_modal: imgui.open_popup("Track Proposal") diff --git a/tests/test_gui_context_presets.py b/tests/test_gui_context_presets.py new file mode 100644 index 0000000..eff65eb --- /dev/null +++ b/tests/test_gui_context_presets.py @@ -0,0 +1,35 @@ +import pytest +import time +from src.api_hook_client import ApiHookClient + +def test_gui_context_preset_save_load(live_gui) -> None: + """Verify that saving and loading context presets works via the GUI app.""" + client = ApiHookClient() + assert client.wait_for_server(timeout=15) + + preset_name = "test_gui_preset" + test_files = ["test.py"] + test_screenshots = ["test.png"] + + client.push_event("custom_callback", {"callback": "simulate_save_preset", "args": [preset_name]}) + time.sleep(1.5) + + project_data = client.get_project() + project = project_data.get("project", {}) + presets = project.get("context_presets", {}) + + assert preset_name in presets, f"Preset '{preset_name}' not found in project context_presets" + + preset_entry = presets[preset_name] + preset_files = [f["path"] if isinstance(f, dict) else str(f) for f in preset_entry.get("files", [])] + assert preset_files == test_files + assert preset_entry.get("screenshots", []) == test_screenshots + + # Load the preset + client.push_event("custom_callback", {"callback": "load_context_preset", "args": [preset_name]}) + time.sleep(1.0) + + context = client.get_context_state() + loaded_files = [f["path"] if isinstance(f, dict) else str(f) for f in context.get("files", [])] + assert loaded_files == test_files + assert context.get("screenshots", []) == test_screenshots