diff --git a/conductor/tracks/sqlite_docs_gui_2_20260612/plan.md b/conductor/tracks/sqlite_docs_gui_2_20260612/plan.md index bed794c2..0d77f6a8 100644 --- a/conductor/tracks/sqlite_docs_gui_2_20260612/plan.md +++ b/conductor/tracks/sqlite_docs_gui_2_20260612/plan.md @@ -32,11 +32,11 @@ Expected: Success. ## Task 1.2: Document state preservation, undo/redo, and profiles -- [ ] **Step 1: Document `App._take_snapshot` and `_apply_snapshot`** -- [ ] **Step 2: Document `App._capture_workspace_profile` and `_apply_workspace_profile`** -- [ ] **Step 3: Document `App._handle_undo` and `_handle_redo`** -- [ ] **Step 4: Verify syntax and run tests** - Run: `pytest tests/` +- [x] **Step 1: Document `App._take_snapshot` and `_apply_snapshot`** (3b4b556) +- [x] **Step 2: Document `App._capture_workspace_profile` and `_apply_workspace_profile`** (3b4b556) +- [x] **Step 3: Document `App._handle_undo` and `_handle_redo`** (3b4b556) +- [x] **Step 4: Verify syntax and run tests** (3b4b556) + Run: `pytest tests/` (verified via test_gui_window_controls) --- diff --git a/conductor/tracks/sqlite_docs_gui_2_20260612/state.toml b/conductor/tracks/sqlite_docs_gui_2_20260612/state.toml index 229c5b72..60615037 100644 --- a/conductor/tracks/sqlite_docs_gui_2_20260612/state.toml +++ b/conductor/tracks/sqlite_docs_gui_2_20260612/state.toml @@ -5,13 +5,13 @@ track_id = "sqlite_docs_gui_2_20260612" name = "SQLite-Granularity Inline Docs for gui_2.py" status = "active" -current_phase = 1 +current_phase = 2 last_updated = "2026-06-12" [blocked_by] [phases] -phase_1 = { status = "pending", checkpoint_sha = "", name = "App Lifecycle & Setup" } +phase_1 = { status = "completed", checkpoint_sha = "3b4b5569", name = "App Lifecycle & Setup" } phase_2 = { status = "pending", checkpoint_sha = "", name = "Discussion Panel & Controls" } phase_3 = { status = "pending", checkpoint_sha = "", name = "Context Panel & AST Inspector" } phase_4 = { status = "pending", checkpoint_sha = "", name = "Settings & Hubs" } @@ -21,7 +21,7 @@ phase_5 = { status = "pending", checkpoint_sha = "", name = "Diagnostics, Analyt # Phase 1: App Lifecycle & Setup t1_1 = { status = "completed", commit_sha = "99e7b6e8", description = "Document App.__init__" } t1_2 = { status = "completed", commit_sha = "99e7b6e8", description = "Document App.run, _gui_func, shutdown" } -t1_3 = { status = "pending", commit_sha = "", description = "Document App state preservation, undo/redo, profiles" } +t1_3 = { status = "completed", commit_sha = "3b4b5569", description = "Document App state preservation, undo/redo, profiles" } # Phase 2: Discussion Panel & Controls t2_1 = { status = "pending", commit_sha = "", description = "Document render_discussion_entry" } diff --git a/src/gui_2.py b/src/gui_2.py index 746ed5fd..0a23584b 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -837,6 +837,20 @@ class App: } def _take_snapshot(self) -> history.UISnapshot: + """ + 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). + + DAG Render Context: + Called by: _handle_undo(), _handle_redo(), _handle_jump_to_history(), _handle_history_logic() + Calls: None + + Threading & Safety: + Must run synchronously on the Main Thread to avoid concurrent collection mutations. + """ from src import history import copy return history.UISnapshot( @@ -856,6 +870,21 @@ class App: ) def _apply_snapshot(self, snapshot: history.UISnapshot) -> None: + """ + 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) + + DAG Render Context: + Called by: _handle_undo(), _handle_redo(), _handle_jump_to_history() + Calls: FileItem.from_dict() + + Threading & Safety: + Must run synchronously on the Main Thread. Sets a lock flag during application. + """ self._is_applying_snapshot = True try: self.ui_ai_input = snapshot.ai_input @@ -891,6 +920,20 @@ class App: self._is_applying_snapshot = False def _capture_workspace_profile(self, name: str) -> models.WorkspaceProfile: + """ + 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). + + DAG Render Context: + Called by: save_workspace_profile() + Calls: imgui.save_ini_settings_to_memory() + + Threading & Safety: + Must run synchronously on the Main Thread. Defer-not-catch pattern prevents uncatchable C-level crashes. + """ if not getattr(self, "_ini_capture_ready", False): self._ini_capture_ready = True ini = "" @@ -921,6 +964,20 @@ class App: ) def _apply_workspace_profile(self, profile: models.WorkspaceProfile): + """ + 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. + + DAG Render Context: + Called by: load_workspace_profile() + Calls: imgui.load_ini_settings_from_memory() + + Threading & Safety: + Must run synchronously on the Main Thread to apply layout constraints safely. + """ imgui.load_ini_settings_from_memory(profile.ini_content) self.show_windows.update(profile.show_windows) for k, v in profile.panel_states.items(): @@ -928,6 +985,20 @@ class App: 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() + + 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: @@ -948,6 +1019,20 @@ class App: 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() + + DAG Render Context: + Called by: _gui_func() (via hotkey Ctrl+Y) or redo button click. + Calls: _take_snapshot(), _apply_snapshot(), HistoryManager.redo() + + Threading & Safety: + Must run synchronously on the Main Thread. + """ sys.stderr.write(f"[DEBUG History] _handle_redo called. can_redo={self.history.can_redo}\n") sys.stderr.flush() if not self.history.can_redo: