# tab_manager.py # # Copyright 2025 Pavel Baksy # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # SPDX-License-Identifier: GPL-3.0-or-later from typing import List, Optional import uuid import json import logging from pathlib import Path from .models import RequestTab, HttpRequest, HttpResponse logger = logging.getLogger(__name__) class TabManager: """Manages open request tabs.""" def __init__(self): self.tabs: List[RequestTab] = [] self.active_tab_id: Optional[str] = None def create_tab(self, name: str, request: HttpRequest, saved_request_id: Optional[str] = None, response: Optional[HttpResponse] = None, project_id: Optional[str] = None, selected_environment_id: Optional[str] = None, scripts=None) -> RequestTab: """Create a new tab and add it to the collection.""" tab_id = str(uuid.uuid4()) # Store original request for change detection # Make a copy to avoid reference issues # Create original for saved requests or loaded history items (anything with content) # Only skip for truly blank new requests has_content = bool(request.url or request.body or request.headers) original_request = HttpRequest( method=request.method, url=request.url, headers=request.headers.copy(), body=request.body, syntax=request.syntax ) if has_content else None tab = RequestTab( id=tab_id, name=name, request=request, response=response, saved_request_id=saved_request_id, modified=False, original_request=original_request, project_id=project_id, selected_environment_id=selected_environment_id, scripts=scripts ) self.tabs.append(tab) self.active_tab_id = tab_id return tab def close_tab(self, tab_id: str) -> bool: """Close a tab by ID. Returns True if successful.""" tab = self.get_tab_by_id(tab_id) if not tab: return False self.tabs.remove(tab) # Update active tab if we closed the active one if self.active_tab_id == tab_id: if self.tabs: self.active_tab_id = self.tabs[-1].id # Switch to last tab else: self.active_tab_id = None return True def get_active_tab(self) -> Optional[RequestTab]: """Get the currently active tab.""" if not self.active_tab_id: return None return self.get_tab_by_id(self.active_tab_id) def set_active_tab(self, tab_id: str) -> None: """Set the active tab by ID.""" if self.get_tab_by_id(tab_id): self.active_tab_id = tab_id def get_tab_by_id(self, tab_id: str) -> Optional[RequestTab]: """Get a tab by its ID.""" for tab in self.tabs: if tab.id == tab_id: return tab return None def has_modified_tabs(self) -> bool: """Check if any tabs have unsaved changes.""" return any(tab.is_modified() for tab in self.tabs) def get_tab_index(self, tab_id: str) -> int: """Get the index of a tab by ID. Returns -1 if not found.""" for i, tab in enumerate(self.tabs): if tab.id == tab_id: return i return -1 def save_session(self, filepath: str) -> None: """Save all tabs to a JSON file for session persistence.""" session_data = { 'tabs': [tab.to_dict() for tab in self.tabs], 'active_tab_id': self.active_tab_id } # Ensure directory exists path = Path(filepath) path.parent.mkdir(parents=True, exist_ok=True) with open(filepath, 'w') as f: json.dump(session_data, f, indent=2) def load_session(self, filepath: str) -> None: """Load tabs from a JSON file to restore session.""" try: with open(filepath, 'r') as f: session_data = json.load(f) self.tabs = [RequestTab.from_dict(tab_data) for tab_data in session_data.get('tabs', [])] self.active_tab_id = session_data.get('active_tab_id') # Validate active_tab_id still exists if self.active_tab_id and not self.get_tab_by_id(self.active_tab_id): self.active_tab_id = self.tabs[0].id if self.tabs else None except (FileNotFoundError, json.JSONDecodeError, KeyError) as e: # If session file is invalid, start fresh logger.warning(f"Failed to load session: {e}") self.tabs = [] self.active_tab_id = None