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>
This commit is contained in:
2026-03-28 17:34:18 +05:30
parent 1dbbb4320b
commit 01662f7e0e
37 changed files with 7822 additions and 1 deletions

32
app/ui/highlighter.py Normal file
View File

@@ -0,0 +1,32 @@
"""Simple JSON/general syntax highlighter for QTextEdit."""
import re
from PyQt6.QtGui import QSyntaxHighlighter, QTextCharFormat, QColor, QFont
class JsonHighlighter(QSyntaxHighlighter):
def __init__(self, parent=None):
super().__init__(parent)
self._rules = []
def fmt(color, bold=False):
f = QTextCharFormat()
f.setForeground(QColor(color))
if bold:
f.setFontWeight(QFont.Weight.Bold)
return f
# Keys
self._rules.append((re.compile(r'"([^"\\]|\\.)*"\s*(?=:)'), fmt("#9CDCFE")))
# String values
self._rules.append((re.compile(r'(?<!:)\s*"([^"\\]|\\.)*"'), fmt("#CE9178")))
# Numbers
self._rules.append((re.compile(r'\b-?\d+(\.\d+)?([eE][+-]?\d+)?\b'), fmt("#B5CEA8")))
# Booleans & null
self._rules.append((re.compile(r'\b(true|false|null)\b'), fmt("#569CD6", bold=True)))
# Braces/brackets
self._rules.append((re.compile(r'[{}\[\]]'), fmt("#FFD700")))
def highlightBlock(self, text):
for pattern, fmt in self._rules:
for m in pattern.finditer(text):
self.setFormat(m.start(), m.end() - m.start(), fmt)