Files
APIClient-Agent/app/ui/tabs_manager.py
Anand Shukla 01662f7e0e Initial release — APIClient - Agent v2.0.0
AI-first API testing desktop client built with Python + PyQt6.

Features:
- Multi-tab HTTP request editor with params/headers/body/auth/tests
- KeyValueTable with per-row enable/disable checkboxes and 36px rows
- Format JSON button, syntax highlighting, pre-request & test scripts
- Collections, environments, history, import/export (Postman v2.1, cURL)
- OpenAPI 3.x / Swagger 2.0 local parser (no AI tokens)
- EKIKA Odoo API Framework generator — JSON-API, REST JSON, GraphQL,
  Custom REST JSON with all auth types (instant, no AI tokens)
- Persistent AI chat sidebar (Claude-powered co-pilot) with streaming,
  context-aware suggestions, and one-click Apply to request editor
- AI collection generator from any docs URL or pasted spec
- WebSocket client, Mock server, Collection runner, Code generator
- Dark/light theme engine (global QSS, object-name selectors)
- SSL error detection with actionable hints
- MIT License

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 17:38:57 +05:30

98 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""APIClient - Agent — Multi-tab request manager."""
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QTabWidget, QPushButton, QTabBar
from PyQt6.QtCore import pyqtSignal, Qt
from app.ui.request_panel import RequestPanel
from app.models import HttpRequest
class RequestTab(QWidget):
send_requested = pyqtSignal(object)
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
self.request_panel = RequestPanel()
self.request_panel.send_requested.connect(self.send_requested)
layout.addWidget(self.request_panel)
def load_request(self, req: HttpRequest):
self.request_panel.load_request(req)
def get_request(self) -> HttpRequest:
return self.request_panel.get_request()
class TabsManager(QWidget):
send_requested = pyqtSignal(object)
current_tab_changed = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
self.tab_widget = QTabWidget()
self.tab_widget.setTabsClosable(False) # we manage close buttons ourselves
self.tab_widget.setMovable(True)
self.tab_widget.currentChanged.connect(lambda _: self.current_tab_changed.emit())
# "+" new tab button in the corner
new_btn = QPushButton("+")
new_btn.setObjectName("ghost")
new_btn.setFixedSize(28, 28)
new_btn.setToolTip("New Tab (Ctrl+T)")
new_btn.clicked.connect(lambda: self.new_tab())
self.tab_widget.setCornerWidget(new_btn, Qt.Corner.TopRightCorner)
layout.addWidget(self.tab_widget)
self._tab_counter = 0
self.new_tab()
# ── Public API ────────────────────────────────────────────────────────────
def new_tab(self, req: HttpRequest = None) -> RequestTab:
tab = RequestTab()
tab.send_requested.connect(self.send_requested)
if req:
tab.load_request(req)
self._tab_counter += 1
label = req.name if (req and req.name) else f"Request {self._tab_counter}"
idx = self.tab_widget.addTab(tab, label)
self.tab_widget.setCurrentIndex(idx)
self._add_close_button(idx, tab)
return tab
def _add_close_button(self, idx: int, tab: RequestTab):
btn = QPushButton("×")
btn.setObjectName("tabCloseBtn")
btn.setFixedSize(18, 18)
btn.setToolTip("Close Tab")
btn.clicked.connect(lambda: self._close_tab(self.tab_widget.indexOf(tab)))
self.tab_widget.tabBar().setTabButton(idx, QTabBar.ButtonPosition.RightSide, btn)
def close_current_tab(self):
self._close_tab(self.tab_widget.currentIndex())
def _close_tab(self, index: int):
if self.tab_widget.count() > 1:
self.tab_widget.removeTab(index)
def current_tab(self) -> RequestTab | None:
w = self.tab_widget.currentWidget()
return w if isinstance(w, RequestTab) else None
def load_request_in_new_tab(self, req: HttpRequest):
self.new_tab(req)
def load_request_in_current_tab(self, req: HttpRequest):
tab = self.current_tab()
if tab:
tab.load_request(req)
def rename_current_tab(self, name: str):
self.tab_widget.setTabText(self.tab_widget.currentIndex(), name)