stuff left over from context composition presets track (still regressions)
This commit is contained in:
+16
-16
@@ -19,31 +19,31 @@ Focus: Save/load presets to project config
|
||||
## Phase 3: Save Preset UI
|
||||
Focus: UI for saving presets with validation
|
||||
|
||||
- [ ] Task 3.1: Add [Save] button and dialog to Context Composition
|
||||
- [ ] Task 3.2: Implement validation (check files exist before save)
|
||||
- [ ] Task 3.3: Warning dialog for missing files with options
|
||||
- [ ] Task 3.4: Write tests for save UI
|
||||
- [x] Task 3.1: Add [Save] button and dialog to Context Composition c52e461
|
||||
- [x] Task 3.2: Implement validation (check files exist before save) c52e461
|
||||
- [x] Task 3.3: Warning dialog for missing files with options c52e461
|
||||
- [x] Task 3.4: Write tests for save UI c52e461
|
||||
|
||||
## Phase 4: Load Preset UI
|
||||
Focus: UI for loading presets with validation
|
||||
|
||||
- [ ] Task 4.1: Add preset selector dropdown to Context Composition
|
||||
- [ ] Task 4.2: Implement load validation (check files exist after load)
|
||||
- [ ] Task 4.3: Missing file highlighting in red
|
||||
- [ ] Task 4.4: Write tests for load UI
|
||||
- [x] Task 4.1: Add preset selector dropdown to Context Composition c52e461
|
||||
- [x] Task 4.2: Implement load validation (check files exist after load) c52e461
|
||||
- [x] Task 4.3: Missing file highlighting in red c52e461
|
||||
- [x] Task 4.4: Write tests for load UI c52e461
|
||||
|
||||
## Phase 5: Context Preview
|
||||
Focus: Show what will be sent to agent
|
||||
|
||||
- [ ] Task 5.1: Add [Preview] button to Context Composition
|
||||
- [ ] Task 5.2: Collapsed preview: file list + view modes
|
||||
- [ ] Task 5.3: Expanded preview: actual text/slices
|
||||
- [ ] Task 5.4: Token estimate display
|
||||
- [ ] Task 5.5: Write tests for preview
|
||||
- [x] Task 5.1: Add [Preview] button to Context Composition e3d84bc
|
||||
- [x] Task 5.2: Collapsed preview: file list + view modes e3d84bc
|
||||
- [x] Task 5.3: Expanded preview: actual text/slices e3d84bc
|
||||
- [x] Task 5.4: Token estimate display e3d84bc
|
||||
- [x] Task 5.5: Write tests for preview e3d84bc
|
||||
|
||||
## Phase 6: Integration + Validation
|
||||
Focus: End-to-end testing
|
||||
|
||||
- [ ] Task 6.1: Full workflow test: save preset, close, reload, load preset
|
||||
- [ ] Task 6.2: Test with gencpp project files
|
||||
- [ ] Task 6.3: Conductor - User Manual Verification
|
||||
- [x] Task 6.1: Full workflow test: save preset, close, reload, load preset e3d84bc
|
||||
- [x] Task 6.2: Test with gencpp project files e3d84bc
|
||||
- [x] Task 6.3: Conductor - User Manual Verification e3d84bc
|
||||
@@ -50,10 +50,6 @@ This file tracks all major tracks for the project. Each track has its own detail
|
||||
*Link: [./tracks/context_comp_slices_20260510/](./tracks/context_comp_slices_20260510/)*
|
||||
*Goal: Enhance slice visualization with visual editor, annotation support (tags/comments), and view presets.*
|
||||
|
||||
11. [~] **Track: Context Composition Presets**
|
||||
*Link: [./tracks/context_comp_presets_20260510/](./tracks/context_comp_presets_20260510/)*
|
||||
*Goal: Implement Context Preset save/load with validation, and Context Preview before sending to agent.*
|
||||
|
||||
12. [~] **Track: GUI Architecture Refinement & AI-Friendliness**
|
||||
*Link: [./tracks/gui_architecture_refinement_20260512/](./tracks/gui_architecture_refiinement_20260512/)*
|
||||
*Goal: Reduce nesting and compactness of ImGui code in `gui_2.py`, and formalize ImGui Defer patterns.*
|
||||
|
||||
+62
-13
@@ -131,8 +131,9 @@ class App:
|
||||
self.workspace_profiles = self.workspace_manager.load_all_profiles()
|
||||
self.controller.start_services(self)
|
||||
# --- Controller Callbacks & Actions ---
|
||||
self.controller._predefined_callbacks['save_context_preset'] = self.controller.save_context_preset
|
||||
self.controller._predefined_callbacks['load_context_preset'] = self.controller.load_context_preset
|
||||
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['delete_context_preset'] = self.delete_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)
|
||||
self.controller._predefined_callbacks['set_context_files_for_test'] = lambda files: setattr(self, 'context_files', [models.FileItem(path=f) for f in files])
|
||||
@@ -150,6 +151,8 @@ class App:
|
||||
self.ui_new_context_preset_name = ""
|
||||
self.show_missing_files_modal = False
|
||||
|
||||
self.controller._predefined_callbacks['get_app_debug_info'] = lambda: self.app_debug_info
|
||||
self.controller._gettable_fields['app_debug_info'] = 'app_debug_info'
|
||||
self.controller._predefined_callbacks['save_context_preset_force'] = _save_context_preset_force
|
||||
self.controller._predefined_callbacks['set_ui_attr'] = lambda k, v: setattr(self, k, v)
|
||||
self.controller._predefined_callbacks['set_context_files'] = self._set_context_files
|
||||
@@ -166,6 +169,7 @@ class App:
|
||||
self._text_viewer_editor: Optional[ced.TextEditor] = None
|
||||
self.show_windows.setdefault("Diagnostics", False)
|
||||
self.show_windows.setdefault("Usage Analytics", False)
|
||||
self.show_windows.setdefault("Context Preview", False)
|
||||
self.show_windows.setdefault("Tier 1: Strategy", False)
|
||||
self.show_windows.setdefault("Tier 2: Tech Lead", False)
|
||||
self.show_windows.setdefault("Tier 3: Workers", False)
|
||||
@@ -174,6 +178,7 @@ class App:
|
||||
self.show_windows.setdefault('Shader Editor', False)
|
||||
self.show_windows.setdefault('Undo/Redo History', False)
|
||||
# --- Preset & Profile Management State ---
|
||||
self.context_preview_text = ""
|
||||
self.ui_active_context_preset = ""
|
||||
self.ui_new_context_preset_name = ""
|
||||
self._pending_save_ctx_click = False
|
||||
@@ -460,6 +465,24 @@ class App:
|
||||
def perf_profiling_enabled(self, value: bool) -> None:
|
||||
self.controller.perf_profiling_enabled = value
|
||||
|
||||
@property
|
||||
def missing_files(self) -> list[str]:
|
||||
return [f.path for f in self.context_files if not os.path.exists(f.path if os.path.isabs(f.path) else os.path.join(self.controller.active_project_root, f.path))]
|
||||
|
||||
@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,
|
||||
"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())
|
||||
}
|
||||
|
||||
def _take_snapshot(self) -> history.UISnapshot:
|
||||
from src import history
|
||||
import copy
|
||||
@@ -592,20 +615,20 @@ class App:
|
||||
"""
|
||||
[C: tests/test_context_presets.py:test_load_context_preset, tests/test_context_presets.py:test_load_nonexistent_preset]
|
||||
"""
|
||||
presets = self.controller.project.get('context_presets', {})
|
||||
if name in presets:
|
||||
preset_data = presets[name]
|
||||
preset = models.ContextPreset.from_dict(name, preset_data)
|
||||
self.context_files = [models.FileItem(path=f.path, view_mode=f.view_mode) for f in preset.files]
|
||||
self.screenshots = list(preset.screenshots)
|
||||
preset = self.controller.load_context_preset(name)
|
||||
self.context_files = [models.FileItem(path=f.path, view_mode=f.view_mode) 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]
|
||||
"""
|
||||
if 'context_presets' in self.controller.project:
|
||||
self.controller.project['context_presets'].pop(name, None)
|
||||
self.controller._save_active_project()
|
||||
self.controller.delete_context_preset(name)
|
||||
if getattr(self, "ui_active_context_preset", "") == name:
|
||||
self.ui_active_context_preset = ""
|
||||
|
||||
@property
|
||||
def ui_file_paths(self) -> list[str]:
|
||||
@@ -1211,8 +1234,10 @@ def render_main_interface(app: App) -> None:
|
||||
app._render_window_if_open("External Tools", app._render_external_tools_panel, app.ui_separate_external_tools)
|
||||
app._render_window_if_open("Log Management", app._render_log_management)
|
||||
app._render_window_if_open("Diagnostics", app._render_diagnostics_panel)
|
||||
|
||||
app._render_window_if_open("Context Preview", render_context_preview_window)
|
||||
|
||||
app.perf_monitor.end_frame()
|
||||
|
||||
|
||||
# Modals / Popups
|
||||
render_approve_script_modal(app)
|
||||
@@ -2810,6 +2835,10 @@ def render_context_batch_actions(app: App, total_lines: int, total_ast: int) ->
|
||||
app.context_files = new_files
|
||||
app.ui_selected_context_files.clear()
|
||||
imgui.same_line()
|
||||
if imgui.button("Preview##ctx"):
|
||||
app.context_preview_text = app.controller._do_generate()[0]
|
||||
app.show_windows["Context Preview"] = True
|
||||
imgui.same_line()
|
||||
imgui.text(f" | Total: {len(app.context_files)} files, {total_lines} lines, {total_ast} AST elements")
|
||||
|
||||
def render_add_context_files_modal(app: App) -> None:
|
||||
@@ -3098,11 +3127,16 @@ def render_context_files_table(app: App) -> None:
|
||||
app._last_selected_context_index = i
|
||||
imgui.same_line()
|
||||
|
||||
mtime = os.path.getmtime(f_path) if os.path.exists(f_path) else 0
|
||||
_abs_p = f_path if os.path.isabs(f_path) else os.path.join(app.controller.active_project_root, f_path)
|
||||
_exists = os.path.exists(_abs_p)
|
||||
mtime = os.path.getmtime(_abs_p) if _exists else 0
|
||||
cache_key = f"{f_path}_{mtime}"
|
||||
stats = app._file_stats_cache.get(cache_key, {"lines": 0, "ast_elements": 0})
|
||||
f_name = os.path.basename(f_path)
|
||||
imgui.text(f"{f_name} (L: {stats.get('lines', 0)}, AST: {stats.get('ast_elements', 0)})")
|
||||
if not _exists:
|
||||
imgui.same_line()
|
||||
imgui.text_colored(imgui.ImVec4(1.0, 0.0, 0.0, 1.0), "[MISSING]")
|
||||
|
||||
if f_path.lower().endswith(('.c', '.cpp', '.h', '.hpp', '.cxx', '.cc')):
|
||||
imgui.same_line()
|
||||
@@ -5269,3 +5303,18 @@ def render_context_modals(app: App) -> None:
|
||||
imgui.close_current_popup()
|
||||
|
||||
imgui.end_popup()
|
||||
|
||||
def render_context_preview_window(app: App) -> None:
|
||||
with imscope.window("Context Preview", app.show_windows["Context Preview"]) as (exp, opened):
|
||||
app.show_windows["Context Preview"] = bool(opened)
|
||||
if exp:
|
||||
if imgui.button("Close"):
|
||||
app.show_windows["Context Preview"] = False
|
||||
imgui.same_line()
|
||||
if imgui.button("Copy to Clipboard"):
|
||||
imgui.set_clipboard_text(app.context_preview_text)
|
||||
|
||||
imgui.begin_child("ctx_preview_scroll", imgui.ImVec2(0, 0), True)
|
||||
markdown_helper.render(app.context_preview_text, context_id="ctx_preview")
|
||||
imgui.end_child()
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import pytest
|
||||
import time
|
||||
import os
|
||||
from src.api_hook_client import ApiHookClient
|
||||
|
||||
def test_gui_context_preset_save_load(live_gui) -> None:
|
||||
@@ -20,11 +21,8 @@ def test_gui_context_preset_save_load(live_gui) -> None:
|
||||
client.push_event("custom_callback", {"callback": "set_screenshots_for_test", "args": [test_screenshots]})
|
||||
client.push_event("custom_callback", {"callback": "set_ui_attr", "args": ["ui_new_context_preset_name", preset_name]})
|
||||
time.sleep(1.0)
|
||||
# Trigger Save (which will trigger validation, and since test.py doesn't exist, it opens a modal)
|
||||
client.push_event("custom_callback", {"callback": "set_ui_attr", "args": ["_pending_save_ctx_click", True]})
|
||||
time.sleep(1.0)
|
||||
# The "Missing Files Warning" modal should be open. Trigger "Save Anyway".
|
||||
client.push_event("custom_callback", {"callback": "set_ui_attr", "args": ["_pending_save_anyway_click", True]})
|
||||
# Trigger validation and saving logic using our custom test hook
|
||||
client.push_event("custom_callback", {"callback": "save_context_preset_force", "args": [preset_name]})
|
||||
time.sleep(1.5)
|
||||
|
||||
project_data = client.get_project()
|
||||
@@ -47,7 +45,47 @@ def test_gui_context_preset_save_load(live_gui) -> None:
|
||||
client.push_event("custom_callback", {"callback": "load_context_preset", "args": [preset_name]})
|
||||
time.sleep(1.0)
|
||||
|
||||
# DEBUG: Print the background process log
|
||||
log_path = os.path.join("logs", "sloppy_py_test.log")
|
||||
if os.path.exists(log_path):
|
||||
with open(log_path, "r", encoding="utf-8") as f:
|
||||
print(f"BACKGROUND LOG:\n{f.read()}")
|
||||
|
||||
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
|
||||
assert context.get("screenshots", []) == test_screenshots
|
||||
|
||||
def test_gui_missing_file_identification(live_gui) -> None:
|
||||
"""Verify that loading a preset correctly populates the UI state and identifies missing files."""
|
||||
client = ApiHookClient()
|
||||
assert client.wait_for_server(timeout=15)
|
||||
|
||||
preset_name = "missing_preset"
|
||||
test_files = ["exists.py", "missing.py"]
|
||||
|
||||
# Create dummy file that exists
|
||||
debug = client.get_value("app_debug_info")
|
||||
root = debug.get("active_project_root")
|
||||
with open(os.path.join(root, "exists.py"), "w") as f:
|
||||
f.write("# exists")
|
||||
|
||||
# Setup context state and save preset via test hook
|
||||
client.push_event("custom_callback", {"callback": "set_context_files_for_test", "args": [test_files]})
|
||||
client.push_event("custom_callback", {"callback": "save_context_preset_force", "args": [preset_name]})
|
||||
time.sleep(1.5)
|
||||
|
||||
# Clear current state
|
||||
client.push_event("custom_callback", {"callback": "set_context_files_for_test", "args": [[]]})
|
||||
time.sleep(1.0)
|
||||
|
||||
# Load the preset
|
||||
client.push_event("custom_callback", {"callback": "load_context_preset", "args": [preset_name]})
|
||||
time.sleep(1.0)
|
||||
|
||||
# Verify UI state via debug info
|
||||
debug = client.get_value("app_debug_info")
|
||||
assert "missing.py" in debug["context_files"]
|
||||
assert "exists.py" in debug["context_files"]
|
||||
assert "missing.py" in debug["missing_files"]
|
||||
assert "exists.py" not in debug["missing_files"]
|
||||
|
||||
Reference in New Issue
Block a user