feat(workspace): implement layout capture/restore and controller integration

This commit is contained in:
2026-05-05 21:09:51 -04:00
parent b7ba7a1ef3
commit eab1945035
2 changed files with 52 additions and 1 deletions
+19 -1
View File
@@ -6,6 +6,7 @@ import os
import re import re
from typing import Any, List, Dict, Optional, Callable from typing import Any, List, Dict, Optional, Callable
from pathlib import Path from pathlib import Path
from src import workspace_manager
import json import json
import uuid import uuid
import tomli_w import tomli_w
@@ -635,7 +636,9 @@ class AppController:
'_cb_save_tool_preset': self._cb_save_tool_preset, '_cb_save_tool_preset': self._cb_save_tool_preset,
'_cb_delete_tool_preset': self._cb_delete_tool_preset, '_cb_delete_tool_preset': self._cb_delete_tool_preset,
'_switch_project': self._switch_project, '_switch_project': self._switch_project,
'_refresh_from_project': self._refresh_from_project '_refresh_from_project': self._refresh_from_project,
'save_workspace_profile': self._cb_save_workspace_profile,
'load_workspace_profile': self._cb_load_workspace_profile,
} }
def _update_gcli_adapter(self, path: str) -> None: def _update_gcli_adapter(self, path: str) -> None:
@@ -1038,6 +1041,8 @@ class AppController:
self.project_paths = list(projects_cfg.get("paths", [])) self.project_paths = list(projects_cfg.get("paths", []))
self.active_project_path = projects_cfg.get("active", "") self.active_project_path = projects_cfg.get("active", "")
self._load_active_project() self._load_active_project()
self.workspace_manager = workspace_manager.WorkspaceManager(project_root=Path(self.active_project_path).parent if self.active_project_path else None)
self.workspace_profiles = self.workspace_manager.load_all_profiles()
# Deserialize FileItems in files.paths # Deserialize FileItems in files.paths
raw_paths = self.project.get("files", {}).get("paths", []) raw_paths = self.project.get("files", {}).get("paths", [])
self.files = [] self.files = []
@@ -2250,6 +2255,19 @@ class AppController:
if self.rag_config and self.rag_config.enabled: if self.rag_config and self.rag_config.enabled:
self._rebuild_rag_index() self._rebuild_rag_index()
def _cb_save_workspace_profile(self, name: str, scope: str = 'project') -> None:
if not hasattr(self, '_app') or not self._app:
return
profile = self._app._capture_workspace_profile(name)
self.workspace_manager.save_profile(profile, scope=scope)
self.workspace_profiles = self.workspace_manager.load_all_profiles()
def _cb_load_workspace_profile(self, name: str) -> None:
if name in self.workspace_profiles:
profile = self.workspace_profiles[name]
if hasattr(self, '_app') and self._app:
self._app._apply_workspace_profile(profile)
def _apply_preset(self, name: str, scope: str) -> None: def _apply_preset(self, name: str, scope: str) -> None:
print(f"[DEBUG] _apply_preset: name={name}, scope={scope}") print(f"[DEBUG] _apply_preset: name={name}, scope={scope}")
if name == "None": if name == "None":
+33
View File
@@ -25,6 +25,7 @@ from src import log_registry
from src import log_pruner from src import log_pruner
from src import models from src import models
from src import app_controller from src import app_controller
from src import workspace_manager
from src import mcp_client from src import mcp_client
from src import aggregate from src import aggregate
from src import markdown_helper from src import markdown_helper
@@ -103,6 +104,7 @@ class App:
def __init__(self) -> None: def __init__(self) -> None:
# Initialize controller and delegate state # Initialize controller and delegate state
self.controller = app_controller.AppController() self.controller = app_controller.AppController()
self.controller._app = self
from src import history from src import history
self.history = history.HistoryManager(max_capacity=100) self.history = history.HistoryManager(max_capacity=100)
self._last_ui_snapshot: Optional[history.UISnapshot] = None self._last_ui_snapshot: Optional[history.UISnapshot] = None
@@ -115,6 +117,8 @@ class App:
if not hasattr(self.controller, 'PROVIDERS'): if not hasattr(self.controller, 'PROVIDERS'):
self.controller.PROVIDERS = PROVIDERS self.controller.PROVIDERS = PROVIDERS
self.controller.init_state() self.controller.init_state()
self.workspace_manager = workspace_manager.WorkspaceManager(project_root=self.active_project_root)
self.workspace_profiles = self.workspace_manager.load_all_profiles()
self.show_windows.setdefault("Diagnostics", False) self.show_windows.setdefault("Diagnostics", False)
self.controller.start_services(self) self.controller.start_services(self)
self.controller._predefined_callbacks['_render_text_viewer'] = self._render_text_viewer self.controller._predefined_callbacks['_render_text_viewer'] = self._render_text_viewer
@@ -346,6 +350,35 @@ class App:
finally: finally:
self._is_applying_snapshot = False self._is_applying_snapshot = False
def _capture_workspace_profile(self, name: str) -> models.WorkspaceProfile:
ini = imgui.save_ini_settings_to_memory()
panel_states = {
"ui_separate_message_panel": getattr(self, "ui_separate_message_panel", False),
"ui_separate_response_panel": getattr(self, "ui_separate_response_panel", False),
"ui_separate_tool_calls_panel": getattr(self, "ui_separate_tool_calls_panel", False),
"ui_separate_task_dag": getattr(self, "ui_separate_task_dag", False),
"ui_separate_usage_analytics": getattr(self, "ui_separate_usage_analytics", False),
"ui_separate_tier1": getattr(self, "ui_separate_tier1", False),
"ui_separate_tier2": getattr(self, "ui_separate_tier2", False),
"ui_separate_tier3": getattr(self, "ui_separate_tier3", False),
"ui_separate_tier4": getattr(self, "ui_separate_tier4", False),
"ui_separate_external_tools": getattr(self, "ui_separate_external_tools", False),
"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
)
def _apply_workspace_profile(self, profile: models.WorkspaceProfile):
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)
def _handle_undo(self) -> None: def _handle_undo(self) -> None:
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()