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>
33 lines
1.2 KiB
Python
33 lines
1.2 KiB
Python
"""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)
|