Add comprehensive application redesign draft and full API documentation
- Application redesign with clean architecture, v3.0 roadmap - Complete API documentation with examples - Plugin system redesign - Security architecture - Implementation roadmap
This commit is contained in:
parent
bf1214b3ca
commit
71803350ea
|
|
@ -0,0 +1,894 @@
|
|||
# EU-Utility Complete API Documentation
|
||||
## Version 2.1.0
|
||||
|
||||
---
|
||||
|
||||
## 📖 Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Core APIs](#core-apis)
|
||||
3. [Plugin API](#plugin-api)
|
||||
4. [Widget API](#widget-api)
|
||||
5. [External API](#external-api)
|
||||
6. [Nexus API Integration](#nexus-api-integration)
|
||||
7. [Event System](#event-system)
|
||||
8. [Data Storage](#data-storage)
|
||||
9. [Security](#security)
|
||||
10. [Examples](#examples)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
EU-Utility provides a three-tier API architecture:
|
||||
|
||||
| API | Purpose | Audience |
|
||||
|-----|---------|----------|
|
||||
| **PluginAPI** | Access core services | Plugin developers |
|
||||
| **WidgetAPI** | Create overlay widgets | Widget developers |
|
||||
| **ExternalAPI** | Third-party integrations | External apps |
|
||||
|
||||
---
|
||||
|
||||
## Core APIs
|
||||
|
||||
### Service Locator
|
||||
|
||||
```python
|
||||
from core.api import get_api, get_widget_api, get_external_api
|
||||
|
||||
# Get APIs
|
||||
plugin_api = get_api()
|
||||
widget_api = get_widget_api()
|
||||
external_api = get_external_api()
|
||||
```
|
||||
|
||||
### Available Services
|
||||
|
||||
| Service | Method | Description |
|
||||
|---------|--------|-------------|
|
||||
| Log Reader | `get_log_reader()` | Read game chat logs |
|
||||
| Window Manager | `get_window_manager()` | Control EU window |
|
||||
| OCR | `get_ocr_service()` | Text recognition |
|
||||
| Screenshot | `get_screenshot_service()` | Screen capture |
|
||||
| Nexus API | `get_nexus_api()` | Item database |
|
||||
| HTTP Client | `get_http_client()` | Web requests |
|
||||
| Audio | `get_audio_manager()` | Sound playback |
|
||||
| Notifications | `get_notification_manager()` | Toast notifications |
|
||||
| Clipboard | `get_clipboard_manager()` | Copy/paste |
|
||||
| Event Bus | `get_event_bus()` | Pub/sub events |
|
||||
| Data Store | `get_data_store()` | Key-value storage |
|
||||
| Task Manager | `get_task_manager()` | Background tasks |
|
||||
|
||||
---
|
||||
|
||||
## Plugin API
|
||||
|
||||
### Initialization
|
||||
|
||||
```python
|
||||
from plugins.base_plugin import BasePlugin
|
||||
from core.api import get_api
|
||||
|
||||
class MyPlugin(BasePlugin):
|
||||
def initialize(self):
|
||||
"""Called when plugin loads."""
|
||||
self.api = get_api()
|
||||
self.log_info(f"{self.name} initialized")
|
||||
```
|
||||
|
||||
### Log Reader
|
||||
|
||||
```python
|
||||
# Read last N log lines
|
||||
lines = api.read_log_lines(100)
|
||||
|
||||
# Read since timestamp
|
||||
from datetime import datetime, timedelta
|
||||
recent = api.read_log_since(datetime.now() - timedelta(minutes=5))
|
||||
|
||||
# Read filtered by pattern
|
||||
loot_lines = api.read_log_filtered(pattern="Loot:.*(Uber|Jackpot)")
|
||||
```
|
||||
|
||||
**Returns:** `List[str]` - Log lines
|
||||
|
||||
### Window Manager
|
||||
|
||||
```python
|
||||
# Get EU window info
|
||||
window = api.get_eu_window()
|
||||
if window:
|
||||
print(f"Position: ({window['x']}, {window['y']})")
|
||||
print(f"Size: {window['width']}x{window['height']}")
|
||||
print(f"Focused: {window['is_focused']}")
|
||||
|
||||
# Check if EU is focused
|
||||
if api.is_eu_focused():
|
||||
api.play_sound("alert.wav")
|
||||
|
||||
# Bring EU to front
|
||||
api.bring_eu_to_front()
|
||||
|
||||
# Get window screenshot
|
||||
screenshot = api.capture_eu_window()
|
||||
```
|
||||
|
||||
**Window Info Schema:**
|
||||
```json
|
||||
{
|
||||
"x": 100,
|
||||
"y": 100,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"is_focused": true,
|
||||
"title": "Entropia Universe"
|
||||
}
|
||||
```
|
||||
|
||||
### OCR Service
|
||||
|
||||
```python
|
||||
# Check if OCR is available
|
||||
if api.ocr_available():
|
||||
# Read text from screen region
|
||||
text = api.recognize_text((100, 100, 200, 50))
|
||||
print(f"Found: {text}")
|
||||
|
||||
# Read from EU window specifically
|
||||
eu_text = api.recognize_eu_region("chat")
|
||||
|
||||
# Read with specific engine
|
||||
text = api.recognize_text(
|
||||
region=(100, 100, 200, 50),
|
||||
engine="easyocr" # or "tesseract", "paddleocr"
|
||||
)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `region`: Tuple `(x, y, width, height)`
|
||||
- `engine`: Optional OCR engine name
|
||||
|
||||
**Returns:** `str` - Recognized text
|
||||
|
||||
### Screenshot
|
||||
|
||||
```python
|
||||
# Capture full screen
|
||||
img = api.capture_screen()
|
||||
|
||||
# Capture region
|
||||
img = api.capture_screen((0, 0, 1920, 1080))
|
||||
|
||||
# Save to file
|
||||
img = api.capture_screen(save_path="screenshot.png")
|
||||
|
||||
# Capture EU window
|
||||
img = api.capture_eu_window()
|
||||
|
||||
# Check availability
|
||||
if api.screenshot_available():
|
||||
img = api.capture_screen()
|
||||
```
|
||||
|
||||
**Returns:** `PIL.Image` - Screenshot image
|
||||
|
||||
### Nexus API (Item Database)
|
||||
|
||||
```python
|
||||
# Search items
|
||||
items = api.search_items(
|
||||
query="omegaton",
|
||||
entity_type="weapons", # or "items", "mobs", "all"
|
||||
limit=5
|
||||
)
|
||||
|
||||
for item in items:
|
||||
print(f"{item['name']}: {item['value']} PED")
|
||||
|
||||
# Get item details
|
||||
details = api.get_item_details(item_id=12345)
|
||||
|
||||
# Get market data
|
||||
market = api.get_market_data(item_id=12345)
|
||||
print(f"Current: {market['current_price']} PED")
|
||||
print(f"Markup: {market['markup']}%")
|
||||
|
||||
# Get blueprint info
|
||||
bp = api.get_blueprint_details(bp_id=67890)
|
||||
```
|
||||
|
||||
**Search Response Schema:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "12345",
|
||||
"name": "Omegaton M83 Predator",
|
||||
"type": "weapon",
|
||||
"category": "Rifle",
|
||||
"value": 123.45,
|
||||
"markup": 125.5,
|
||||
"icon_url": "https://..."
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### HTTP Client
|
||||
|
||||
```python
|
||||
# GET request
|
||||
result = api.http_get(
|
||||
url="https://api.example.com/data",
|
||||
params={"key": "value"},
|
||||
cache=True, # Cache response
|
||||
ttl=3600 # Cache for 1 hour
|
||||
)
|
||||
|
||||
if result['success']:
|
||||
data = result['data']
|
||||
print(f"Status: {result['status_code']}")
|
||||
|
||||
# POST request
|
||||
result = api.http_post(
|
||||
url="https://api.example.com/save",
|
||||
data={"key": "value"},
|
||||
headers={"Authorization": "Bearer token"}
|
||||
)
|
||||
|
||||
# With retry
|
||||
result = api.http_get(
|
||||
url="https://api.example.com/data",
|
||||
retry=3,
|
||||
backoff=True
|
||||
)
|
||||
```
|
||||
|
||||
**Response Schema:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {...},
|
||||
"status_code": 200,
|
||||
"cached": false,
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
### Audio
|
||||
|
||||
```python
|
||||
# Play sound file
|
||||
api.play_sound(
|
||||
path="assets/sounds/alert.wav",
|
||||
volume=0.7 # 0.0 to 1.0
|
||||
)
|
||||
|
||||
# Play built-in sound
|
||||
api.play_sound("success")
|
||||
api.play_sound("error")
|
||||
api.play_sound("notification")
|
||||
|
||||
# Simple beep
|
||||
api.beep()
|
||||
|
||||
# Check availability
|
||||
if api.audio_available():
|
||||
api.play_sound("alert.wav")
|
||||
```
|
||||
|
||||
### Notifications
|
||||
|
||||
```python
|
||||
# Show toast notification
|
||||
api.show_notification(
|
||||
title="Loot Alert",
|
||||
message="Found something valuable!",
|
||||
duration=3000, # milliseconds
|
||||
sound=True,
|
||||
icon="loot" # or path to icon
|
||||
)
|
||||
|
||||
# Show with actions
|
||||
api.show_notification(
|
||||
title="Mission Complete",
|
||||
message="You finished the mission!",
|
||||
actions=[
|
||||
{"label": "View", "callback": on_view},
|
||||
{"label": "Dismiss", "callback": on_dismiss}
|
||||
]
|
||||
)
|
||||
|
||||
# Update existing notification
|
||||
notification_id = api.show_notification(...)
|
||||
api.update_notification(
|
||||
notification_id,
|
||||
message="Updated message"
|
||||
)
|
||||
```
|
||||
|
||||
### Clipboard
|
||||
|
||||
```python
|
||||
# Copy to clipboard
|
||||
api.copy_to_clipboard("TT: 100 PED")
|
||||
|
||||
# Copy formatted
|
||||
api.copy_to_clipboard(
|
||||
text="Item: Omegaton",
|
||||
rich_text="<b>Item:</b> Omegaton"
|
||||
)
|
||||
|
||||
# Paste from clipboard
|
||||
text = api.paste_from_clipboard()
|
||||
|
||||
# Check if has content
|
||||
if api.clipboard_has_content():
|
||||
text = api.paste_from_clipboard()
|
||||
```
|
||||
|
||||
### Event Bus
|
||||
|
||||
```python
|
||||
# Subscribe to events
|
||||
def on_loot(event):
|
||||
print(f"Loot: {event.data}")
|
||||
|
||||
sub_id = api.subscribe(
|
||||
event_type="loot.received",
|
||||
handler=on_loot
|
||||
)
|
||||
|
||||
# Subscribe with filter
|
||||
def on_valuable_loot(event):
|
||||
if event.data['value'] > 100:
|
||||
print(f"Uber loot: {event.data}")
|
||||
|
||||
api.subscribe(
|
||||
event_type="loot.received",
|
||||
handler=on_valuable_loot,
|
||||
filter=lambda e: e.data.get('value', 0) > 100
|
||||
)
|
||||
|
||||
# Subscribe once
|
||||
api.subscribe_once(
|
||||
event_type="skill.gain",
|
||||
handler=on_skill_gain
|
||||
)
|
||||
|
||||
# Unsubscribe
|
||||
api.unsubscribe(sub_id)
|
||||
|
||||
# Publish event
|
||||
api.publish(
|
||||
event_type="my_plugin.event",
|
||||
data={"key": "value"}
|
||||
)
|
||||
|
||||
# Typed events
|
||||
from core.event_bus import LootEvent
|
||||
|
||||
def on_loot_typed(event: LootEvent):
|
||||
print(f"From {event.mob_name}: {event.total_tt}")
|
||||
|
||||
api.subscribe_typed(LootEvent, on_loot_typed)
|
||||
```
|
||||
|
||||
**Event Types:**
|
||||
- `loot.received` - Loot event
|
||||
- `skill.gain` - Skill increase
|
||||
- `global.recorded` - Global/HOF
|
||||
- `mission.complete` - Mission finished
|
||||
- `chat.message` - Chat message
|
||||
- `window.focus` - Window focus change
|
||||
|
||||
### Data Store
|
||||
|
||||
```python
|
||||
# Save data
|
||||
api.save_data(
|
||||
key="my_plugin.settings",
|
||||
data={"theme": "dark", "volume": 0.8}
|
||||
)
|
||||
|
||||
# Load data
|
||||
settings = api.load_data(
|
||||
key="my_plugin.settings",
|
||||
default={"theme": "light", "volume": 1.0}
|
||||
)
|
||||
|
||||
# Delete data
|
||||
api.delete_data("my_plugin.settings")
|
||||
|
||||
# List keys
|
||||
keys = api.list_data_keys(pattern="my_plugin.*")
|
||||
|
||||
# Check exists
|
||||
if api.has_data("my_plugin.settings"):
|
||||
settings = api.load_data("my_plugin.settings")
|
||||
```
|
||||
|
||||
### Tasks
|
||||
|
||||
```python
|
||||
# Run async task
|
||||
task_id = api.run_task(
|
||||
func=fetch_market_data,
|
||||
args=(item_id,),
|
||||
callback=on_data_received,
|
||||
error_callback=on_error
|
||||
)
|
||||
|
||||
# Cancel task
|
||||
api.cancel_task(task_id)
|
||||
|
||||
# Schedule task
|
||||
api.schedule_task(
|
||||
func=check_prices,
|
||||
interval=60, # seconds
|
||||
repeat=True
|
||||
)
|
||||
|
||||
# Run in background
|
||||
api.run_in_background(
|
||||
func=heavy_computation,
|
||||
priority="low" # "high", "normal", "low"
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Widget API
|
||||
|
||||
### Creating Widgets
|
||||
|
||||
```python
|
||||
from core.api import get_widget_api
|
||||
|
||||
widget_api = get_widget_api()
|
||||
|
||||
# Create overlay widget
|
||||
widget = widget_api.create_widget(
|
||||
name="Loot Widget",
|
||||
size=(300, 200),
|
||||
position=(100, 100),
|
||||
draggable=True,
|
||||
resizable=True,
|
||||
always_on_top=True
|
||||
)
|
||||
|
||||
# Set content
|
||||
widget.set_content(my_qwidget)
|
||||
|
||||
# Show/hide
|
||||
widget.show()
|
||||
widget.hide()
|
||||
|
||||
# Update position
|
||||
widget.move(200, 200)
|
||||
widget.resize(400, 300)
|
||||
|
||||
# Close
|
||||
widget.close()
|
||||
```
|
||||
|
||||
### Widget Properties
|
||||
|
||||
```python
|
||||
# Transparency
|
||||
widget.set_opacity(0.8) # 0.0 to 1.0
|
||||
|
||||
# Click-through
|
||||
widget.set_click_through(True)
|
||||
|
||||
# Snap to edges
|
||||
widget.set_snap_to_edges(True, margin=10)
|
||||
|
||||
# Auto-hide
|
||||
widget.set_auto_hide(
|
||||
enabled=True,
|
||||
hide_delay=3000,
|
||||
show_on_hover=True
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## External API
|
||||
|
||||
### REST Endpoints
|
||||
|
||||
```python
|
||||
from core.api import get_external_api
|
||||
|
||||
api = get_external_api()
|
||||
|
||||
# Start HTTP server
|
||||
api.start_server(port=8080)
|
||||
|
||||
# Register endpoint
|
||||
@api.endpoint("/loot/recent", methods=["GET"])
|
||||
def get_recent_loot():
|
||||
return {"loot": [...]}
|
||||
|
||||
# With authentication
|
||||
@api.endpoint("/settings", methods=["GET", "POST"], auth_required=True)
|
||||
def settings():
|
||||
if request.method == "POST":
|
||||
api.save_settings(request.json)
|
||||
return api.load_settings()
|
||||
```
|
||||
|
||||
### WebSocket
|
||||
|
||||
```python
|
||||
# WebSocket endpoint
|
||||
@api.websocket("/events")
|
||||
async def events(websocket):
|
||||
async for message in websocket:
|
||||
await websocket.send({"echo": message})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Nexus API Integration
|
||||
|
||||
### Entity Types
|
||||
|
||||
```python
|
||||
from core.nexus_api import EntityType
|
||||
|
||||
# Available types
|
||||
EntityType.ITEM # Items
|
||||
EntityType.WEAPON # Weapons
|
||||
EntityType.ARMOR # Armor
|
||||
EntityType.MOB # Creatures
|
||||
EntityType.BLUEPRINT # Blueprints
|
||||
EntityType.MATERIAL # Materials
|
||||
EntityType.LOCATION # Locations
|
||||
EntityType.TELEPORTER # Teleporters
|
||||
EntityType.SKILL # Skills
|
||||
```
|
||||
|
||||
### Search
|
||||
|
||||
```python
|
||||
nexus = get_nexus_api()
|
||||
|
||||
# Basic search
|
||||
results = nexus.search(
|
||||
query="omegaton",
|
||||
entity_type=EntityType.WEAPON,
|
||||
limit=10
|
||||
)
|
||||
|
||||
# Advanced search
|
||||
results = nexus.search(
|
||||
query="rifle",
|
||||
entity_type=EntityType.WEAPON,
|
||||
filters={
|
||||
"min_damage": 50,
|
||||
"max_damage": 100,
|
||||
"ammo_type": "blp"
|
||||
},
|
||||
sort_by="markup",
|
||||
sort_order="desc",
|
||||
limit=20
|
||||
)
|
||||
```
|
||||
|
||||
### Item Details
|
||||
|
||||
```python
|
||||
# Get full item info
|
||||
item = nexus.get_item(item_id=12345)
|
||||
|
||||
# Get market data
|
||||
market = nexus.get_market_history(
|
||||
item_id=12345,
|
||||
days=30
|
||||
)
|
||||
|
||||
# Get price alerts
|
||||
alerts = nexus.get_price_alerts(item_id=12345)
|
||||
```
|
||||
|
||||
### Blueprints
|
||||
|
||||
```python
|
||||
# Get blueprint details
|
||||
bp = nexus.get_blueprint(bp_id=67890)
|
||||
|
||||
# Get required materials
|
||||
materials = nexus.get_bp_materials(bp_id=67890)
|
||||
|
||||
# Calculate crafting cost
|
||||
cost = nexus.calculate_craft_cost(
|
||||
bp_id=67890,
|
||||
quantity=100,
|
||||
include_markup=True
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Event System
|
||||
|
||||
### Event Structure
|
||||
|
||||
```python
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
|
||||
@dataclass
|
||||
class Event:
|
||||
type: str
|
||||
data: dict
|
||||
timestamp: datetime
|
||||
source: str
|
||||
```
|
||||
|
||||
### Event Types Reference
|
||||
|
||||
| Event Type | Data Schema | Description |
|
||||
|------------|-------------|-------------|
|
||||
| `loot.received` | `{items: [], mob_name: str, total_tt: float}` | Loot received |
|
||||
| `skill.gain` | `{skill: str, old_value: float, new_value: float}` | Skill increase |
|
||||
| `global.recorded` | `{type: str, value: float, mob: str}` | Global/HOF |
|
||||
| `mission.complete` | `{mission_id: str, name: str, rewards: []}` | Mission done |
|
||||
| `chat.message` | `{channel: str, sender: str, message: str}` | Chat message |
|
||||
| `window.focus` | `{window: str, is_focused: bool}` | Focus change |
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
### Permission Model
|
||||
|
||||
```python
|
||||
# Request permissions in plugin manifest
|
||||
permissions = [
|
||||
"log_reader:read",
|
||||
"overlay:create",
|
||||
"data_store:write",
|
||||
"network:external",
|
||||
"ocr:read",
|
||||
"clipboard:read",
|
||||
"clipboard:write"
|
||||
]
|
||||
```
|
||||
|
||||
### Secure Data
|
||||
|
||||
```python
|
||||
# Encrypt sensitive data
|
||||
api.save_secure_data(
|
||||
key="api_keys.nexus",
|
||||
data="secret_key",
|
||||
password=user_password
|
||||
)
|
||||
|
||||
# Decrypt
|
||||
data = api.load_secure_data(
|
||||
key="api_keys.nexus",
|
||||
password=user_password
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Complete Plugin Example
|
||||
|
||||
```python
|
||||
"""
|
||||
Loot Tracker Plugin
|
||||
Tracks hunting loot with analytics.
|
||||
"""
|
||||
|
||||
from PyQt6.QtWidgets import *
|
||||
from PyQt6.QtCore import *
|
||||
from plugins.base_plugin import BasePlugin
|
||||
from core.api import get_api
|
||||
from core.event_bus import LootEvent
|
||||
|
||||
class LootTrackerPlugin(BasePlugin):
|
||||
name = "Loot Tracker"
|
||||
version = "2.0.0"
|
||||
author = "ImpulsiveFPS"
|
||||
description = "Track hunting loot with ROI analysis"
|
||||
hotkey = "ctrl+shift+l"
|
||||
|
||||
def initialize(self):
|
||||
self.api = get_api()
|
||||
self.total_loot = 0.0
|
||||
self.session_start = QDateTime.currentDateTime()
|
||||
|
||||
# Subscribe to loot events
|
||||
self.loot_sub = self.api.subscribe_typed(
|
||||
LootEvent,
|
||||
self.on_loot
|
||||
)
|
||||
|
||||
# Load history
|
||||
self.history = self.api.load_data(
|
||||
"loot_tracker.history",
|
||||
default=[]
|
||||
)
|
||||
|
||||
self.log_info("Loot Tracker initialized")
|
||||
|
||||
def on_loot(self, event: LootEvent):
|
||||
"""Handle loot event."""
|
||||
self.total_loot += event.total_tt
|
||||
|
||||
# Add to history
|
||||
self.history.append({
|
||||
"timestamp": event.timestamp.isoformat(),
|
||||
"mob": event.mob_name,
|
||||
"tt": event.total_tt,
|
||||
"items": len(event.items)
|
||||
})
|
||||
|
||||
# Save (keep last 1000)
|
||||
self.api.save_data(
|
||||
"loot_tracker.history",
|
||||
self.history[-1000:]
|
||||
)
|
||||
|
||||
# Update UI
|
||||
self.update_display()
|
||||
|
||||
# Notify on big loot
|
||||
if event.total_tt > 50:
|
||||
self.api.show_notification(
|
||||
"Big Loot!",
|
||||
f"{event.mob_name}: {event.total_tt:.2f} PED",
|
||||
sound=True
|
||||
)
|
||||
|
||||
def get_ui(self):
|
||||
"""Create plugin UI."""
|
||||
widget = QWidget()
|
||||
layout = QVBoxLayout(widget)
|
||||
|
||||
# Header
|
||||
header = QLabel("Session Loot Tracker")
|
||||
header.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||||
layout.addWidget(header)
|
||||
|
||||
# Stats
|
||||
self.total_label = QLabel(f"Total: {self.total_loot:.2f} PED")
|
||||
self.total_label.setStyleSheet("font-size: 24px; color: #10b981;")
|
||||
layout.addWidget(self.total_label)
|
||||
|
||||
# Session time
|
||||
self.time_label = QLabel("Session: 00:00:00")
|
||||
layout.addWidget(self.time_label)
|
||||
|
||||
# History list
|
||||
self.history_list = QListWidget()
|
||||
layout.addWidget(self.history_list)
|
||||
|
||||
# Buttons
|
||||
btn_layout = QHBoxLayout()
|
||||
|
||||
export_btn = QPushButton("Export")
|
||||
export_btn.clicked.connect(self.export_data)
|
||||
btn_layout.addWidget(export_btn)
|
||||
|
||||
clear_btn = QPushButton("Clear")
|
||||
clear_btn.clicked.connect(self.clear_history)
|
||||
btn_layout.addWidget(clear_btn)
|
||||
|
||||
layout.addLayout(btn_layout)
|
||||
|
||||
# Start update timer
|
||||
self.timer = QTimer()
|
||||
self.timer.timeout.connect(self.update_time)
|
||||
self.timer.start(1000)
|
||||
|
||||
self.update_display()
|
||||
return widget
|
||||
|
||||
def update_display(self):
|
||||
"""Update UI with current stats."""
|
||||
self.total_label.setText(
|
||||
f"Total: {self.total_loot:.2f} PED"
|
||||
)
|
||||
|
||||
# Update history list
|
||||
self.history_list.clear()
|
||||
for entry in reversed(self.history[-50:]):
|
||||
text = f"{entry['mob']}: {entry['tt']:.2f} PED"
|
||||
self.history_list.addItem(text)
|
||||
|
||||
def update_time(self):
|
||||
"""Update session timer."""
|
||||
elapsed = self.session_start.secsTo(
|
||||
QDateTime.currentDateTime()
|
||||
)
|
||||
hours = elapsed // 3600
|
||||
minutes = (elapsed % 3600) // 60
|
||||
seconds = elapsed % 60
|
||||
self.time_label.setText(
|
||||
f"Session: {hours:02d}:{minutes:02d}:{seconds:02d}"
|
||||
)
|
||||
|
||||
def export_data(self):
|
||||
"""Export loot history to CSV."""
|
||||
import csv
|
||||
from pathlib import Path
|
||||
|
||||
path = Path.home() / "loot_history.csv"
|
||||
with open(path, 'w', newline='') as f:
|
||||
writer = csv.DictWriter(f,
|
||||
fieldnames=['timestamp', 'mob', 'tt', 'items'])
|
||||
writer.writeheader()
|
||||
writer.writerows(self.history)
|
||||
|
||||
self.api.show_notification(
|
||||
"Export Complete",
|
||||
f"Saved to {path}"
|
||||
)
|
||||
|
||||
def clear_history(self):
|
||||
"""Clear loot history."""
|
||||
self.history = []
|
||||
self.total_loot = 0.0
|
||||
self.api.delete_data("loot_tracker.history")
|
||||
self.update_display()
|
||||
|
||||
def shutdown(self):
|
||||
"""Cleanup on shutdown."""
|
||||
self.api.unsubscribe(self.loot_sub)
|
||||
self.api.save_data(
|
||||
"loot_tracker.history",
|
||||
self.history[-1000:]
|
||||
)
|
||||
self.log_info("Loot Tracker shutdown")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common Errors
|
||||
|
||||
```python
|
||||
try:
|
||||
result = api.http_get("https://api.example.com")
|
||||
except APIError as e:
|
||||
print(f"API Error: {e.code} - {e.message}")
|
||||
except RateLimitError:
|
||||
print("Rate limited, retrying...")
|
||||
except ServiceNotAvailableError:
|
||||
print("Service not available")
|
||||
```
|
||||
|
||||
### Error Codes
|
||||
|
||||
| Code | Description |
|
||||
|------|-------------|
|
||||
| 1001 | Service not available |
|
||||
| 1002 | Rate limit exceeded |
|
||||
| 1003 | Permission denied |
|
||||
| 1004 | Invalid parameters |
|
||||
| 1005 | Network error |
|
||||
| 1006 | Timeout |
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Changes |
|
||||
|---------|---------|
|
||||
| 2.1.0 | Added WidgetAPI, improved EventBus |
|
||||
| 2.0.0 | Complete API redesign |
|
||||
| 1.5.0 | Added Nexus API integration |
|
||||
| 1.0.0 | Initial release |
|
||||
|
||||
---
|
||||
|
||||
*Complete API Documentation - EU-Utility v2.1.0*
|
||||
*Generated: 2026-02-23*
|
||||
|
|
@ -0,0 +1,713 @@
|
|||
# EU-Utility Application Redesign Draft
|
||||
## Version 3.0 - Professional Architecture
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Executive Summary
|
||||
|
||||
Current EU-Utility is feature-rich but architecture is fragmented. This redesign creates a unified, scalable, maintainable application.
|
||||
|
||||
**Key Improvements:**
|
||||
- Clean Architecture (Domain-Driven Design)
|
||||
- Unified API Gateway
|
||||
- Reactive Event System
|
||||
- Plugin Sandbox Security
|
||||
- Cloud Sync Capabilities
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ New Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ PRESENTATION LAYER │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
||||
│ │ Main UI │ │ Overlay │ │ System Tray │ │
|
||||
│ │ (PyQt6) │ │ Manager │ │ Icon │ │
|
||||
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
|
||||
└─────────┼────────────────┼────────────────────┼─────────────┘
|
||||
│ │ │
|
||||
└────────────────┴────────────────────┘
|
||||
│
|
||||
┌────────▼────────┐
|
||||
│ API GATEWAY │
|
||||
│ (FastAPI/REST) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────────────────────┼────────────────────┐
|
||||
│ │ │
|
||||
┌───────▼──────┐ ┌─────────▼─────────┐ ┌──────▼──────┐
|
||||
│ CORE │ │ PLUGIN ENGINE │ │ SERVICES │
|
||||
│ SERVICES │ │ │ │ │
|
||||
│ │ │ ┌─────────────┐ │ │ • Nexus │
|
||||
│ • Settings │ │ │ Plugin │ │ │ • OCR │
|
||||
│ • DataStore │ │ │ Manager │ │ │ • Overlay │
|
||||
│ • EventBus │ │ └──────┬──────┘ │ │ • Capture │
|
||||
│ • Auth │ │ │ │ └─────────────┘
|
||||
└──────────────┘ │ ┌──────▼──────┐ │
|
||||
│ │ Sandboxed │ │
|
||||
│ │ Plugins │ │
|
||||
│ └─────────────┘ │
|
||||
└───────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 Domain Modules
|
||||
|
||||
### 1. Domain: Core
|
||||
|
||||
```python
|
||||
# domain/core/entities.py
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict, Any
|
||||
from enum import Enum
|
||||
|
||||
class PluginState(Enum):
|
||||
INSTALLED = "installed"
|
||||
ACTIVE = "active"
|
||||
ERROR = "error"
|
||||
DISABLED = "disabled"
|
||||
|
||||
@dataclass
|
||||
class Plugin:
|
||||
"""Core plugin entity."""
|
||||
id: str
|
||||
name: str
|
||||
version: str
|
||||
author: str
|
||||
description: str
|
||||
state: PluginState
|
||||
hotkey: Optional[str] = None
|
||||
config: Dict[str, Any] = None
|
||||
created_at: datetime = None
|
||||
updated_at: datetime = None
|
||||
|
||||
@dataclass
|
||||
class UserSettings:
|
||||
"""User configuration entity."""
|
||||
user_id: str
|
||||
hotkeys: Dict[str, str]
|
||||
theme: str
|
||||
plugins_enabled: list
|
||||
overlay_position: tuple
|
||||
ocr_engine: str
|
||||
api_keys: Dict[str, str]
|
||||
```
|
||||
|
||||
### 2. Domain: Game
|
||||
|
||||
```python
|
||||
# domain/game/entities.py
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
from decimal import Decimal
|
||||
|
||||
@dataclass
|
||||
class LootItem:
|
||||
"""Represents a looted item."""
|
||||
name: str
|
||||
quantity: int
|
||||
tt_value: Decimal
|
||||
market_value: Optional[Decimal] = None
|
||||
|
||||
@dataclass
|
||||
class LootEvent:
|
||||
"""Game loot event."""
|
||||
id: str
|
||||
timestamp: datetime
|
||||
mob_name: str
|
||||
items: List[LootItem]
|
||||
total_tt: Decimal
|
||||
location: Optional[tuple] = None
|
||||
|
||||
@dataclass
|
||||
class SkillGain:
|
||||
"""Skill increase event."""
|
||||
skill_name: str
|
||||
value_before: float
|
||||
value_after: float
|
||||
timestamp: datetime
|
||||
```
|
||||
|
||||
### 3. Domain: Market
|
||||
|
||||
```python
|
||||
# domain/market/entities.py
|
||||
from dataclasses import dataclass
|
||||
from decimal import Decimal
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
@dataclass
|
||||
class MarketItem:
|
||||
"""Market data for an item."""
|
||||
item_id: str
|
||||
name: str
|
||||
category: str
|
||||
current_price: Decimal
|
||||
markup_percent: Decimal
|
||||
volume_24h: int
|
||||
last_update: datetime
|
||||
|
||||
@dataclass
|
||||
class PriceAlert:
|
||||
"""User price alert."""
|
||||
item_id: str
|
||||
user_id: str
|
||||
target_price: Decimal
|
||||
condition: str # 'above' or 'below'
|
||||
is_active: bool
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔌 Plugin System Redesign
|
||||
|
||||
### Plugin Manifest (v3.0)
|
||||
|
||||
```json
|
||||
{
|
||||
"manifest_version": "3.0",
|
||||
"plugin": {
|
||||
"id": "com.example.loot_tracker",
|
||||
"name": "Advanced Loot Tracker",
|
||||
"version": "2.1.0",
|
||||
"author": "ImpulsiveFPS",
|
||||
"description": "Professional loot tracking with analytics",
|
||||
"category": "tracking",
|
||||
"min_api_version": "3.0"
|
||||
},
|
||||
"permissions": [
|
||||
"log_reader:read",
|
||||
"overlay:create",
|
||||
"data_store:write",
|
||||
"notification:send",
|
||||
"ocr:read"
|
||||
],
|
||||
"resources": {
|
||||
"memory_limit": "128MB",
|
||||
"cpu_limit": "10%",
|
||||
"storage_limit": "50MB"
|
||||
},
|
||||
"ui": {
|
||||
"has_overlay": true,
|
||||
"has_settings": true,
|
||||
"hotkey": "ctrl+shift+l",
|
||||
"icon": "treasure-chest"
|
||||
},
|
||||
"dependencies": {
|
||||
"core": ">=3.0",
|
||||
"plugins": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Plugin Base Class
|
||||
|
||||
```python
|
||||
# application/plugin_base.py
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict, Any, Optional
|
||||
from dataclasses import dataclass
|
||||
import asyncio
|
||||
|
||||
@dataclass
|
||||
class PluginContext:
|
||||
"""Context provided to plugins."""
|
||||
api: 'PluginAPI'
|
||||
settings: Dict[str, Any]
|
||||
storage: 'PluginStorage'
|
||||
logger: 'PluginLogger'
|
||||
event_bus: 'EventBus'
|
||||
|
||||
class BasePlugin(ABC):
|
||||
"""Base class for all EU-Utility plugins."""
|
||||
|
||||
# Required - defined in manifest
|
||||
id: str
|
||||
name: str
|
||||
version: str
|
||||
|
||||
# Optional - defined in manifest
|
||||
description: str = ""
|
||||
author: str = ""
|
||||
category: str = "utility"
|
||||
|
||||
def __init__(self, context: PluginContext):
|
||||
self.ctx = context
|
||||
self._state = PluginState.INSTALLED
|
||||
self._config: Dict[str, Any] = {}
|
||||
|
||||
@abstractmethod
|
||||
async def initialize(self) -> bool:
|
||||
"""Initialize the plugin. Return True on success."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def activate(self) -> bool:
|
||||
"""Called when plugin is activated."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def deactivate(self) -> None:
|
||||
"""Called when plugin is deactivated."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_ui(self) -> Optional['QWidget']:
|
||||
"""Return the plugin's UI component."""
|
||||
pass
|
||||
|
||||
async def on_event(self, event: 'Event') -> None:
|
||||
"""Handle events from the event bus."""
|
||||
pass
|
||||
|
||||
def get_config_schema(self) -> Dict[str, Any]:
|
||||
"""Return JSON schema for plugin configuration."""
|
||||
return {}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Security Architecture
|
||||
|
||||
### Permission System
|
||||
|
||||
```python
|
||||
# security/permissions.py
|
||||
from enum import Enum, auto
|
||||
|
||||
class PermissionScope(Enum):
|
||||
"""Permission scopes for plugins."""
|
||||
LOG_READER = auto()
|
||||
OVERLAY = auto()
|
||||
DATA_STORE = auto()
|
||||
NETWORK = auto()
|
||||
OCR = auto()
|
||||
CLIPBOARD = auto()
|
||||
FILE_SYSTEM = auto()
|
||||
PROCESS = auto()
|
||||
|
||||
class PermissionLevel(Enum):
|
||||
"""Permission levels."""
|
||||
NONE = 0
|
||||
READ = 1
|
||||
WRITE = 2
|
||||
ADMIN = 3
|
||||
|
||||
@dataclass
|
||||
class Permission:
|
||||
scope: PermissionScope
|
||||
level: PermissionLevel
|
||||
constraints: Dict[str, Any] = None
|
||||
```
|
||||
|
||||
### Plugin Sandbox
|
||||
|
||||
```python
|
||||
# security/sandbox.py
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
class PluginSandbox:
|
||||
"""Isolated execution environment for plugins."""
|
||||
|
||||
def __init__(self, plugin_id: str, permissions: List[Permission]):
|
||||
self.plugin_id = plugin_id
|
||||
self.permissions = permissions
|
||||
self._temp_dir = tempfile.mkdtemp(prefix=f"eu_plugin_{plugin_id}_")
|
||||
|
||||
def execute(self, code: str) -> Any:
|
||||
"""Execute plugin code in restricted environment."""
|
||||
# Implementation uses restricted Python interpreter
|
||||
# with limited builtins and custom __import__
|
||||
pass
|
||||
|
||||
def cleanup(self) -> None:
|
||||
"""Clean up sandbox resources."""
|
||||
shutil.rmtree(self._temp_dir, ignore_errors=True)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Event System
|
||||
|
||||
### Reactive Event Bus
|
||||
|
||||
```python
|
||||
# infrastructure/event_bus.py
|
||||
from typing import Callable, TypeVar, Generic
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
@dataclass
|
||||
class Event(Generic[T]):
|
||||
"""Generic event container."""
|
||||
type: str
|
||||
payload: T
|
||||
timestamp: datetime
|
||||
source: str
|
||||
|
||||
class ReactiveEventBus:
|
||||
"""Reactive event system with filtering and transformation."""
|
||||
|
||||
def __init__(self):
|
||||
self._subscribers: Dict[str, List[Callable]] = {}
|
||||
self._streams: Dict[str, 'EventStream'] = {}
|
||||
|
||||
def subscribe(self,
|
||||
event_type: str,
|
||||
handler: Callable[[Event], None],
|
||||
filter_fn: Optional[Callable[[Event], bool]] = None) -> str:
|
||||
"""Subscribe to events with optional filtering."""
|
||||
subscription_id = str(uuid.uuid4())
|
||||
# Implementation
|
||||
return subscription_id
|
||||
|
||||
def publish(self, event: Event) -> None:
|
||||
"""Publish event to all subscribers."""
|
||||
# Async dispatch
|
||||
asyncio.create_task(self._dispatch(event))
|
||||
|
||||
def stream(self, event_type: str) -> 'EventStream':
|
||||
"""Get reactive stream for event type."""
|
||||
if event_type not in self._streams:
|
||||
self._streams[event_type] = EventStream(event_type)
|
||||
return self._streams[event_type]
|
||||
|
||||
class EventStream:
|
||||
"""Reactive stream for events."""
|
||||
|
||||
def __init__(self, event_type: str):
|
||||
self.event_type = event_type
|
||||
self._handlers = []
|
||||
|
||||
def filter(self, predicate: Callable[[Event], bool]) -> 'EventStream':
|
||||
"""Filter events."""
|
||||
return self
|
||||
|
||||
def map(self, transform: Callable[[Event], T]) -> 'EventStream':
|
||||
"""Transform events."""
|
||||
return self
|
||||
|
||||
def debounce(self, seconds: float) -> 'EventStream':
|
||||
"""Debounce events."""
|
||||
return self
|
||||
|
||||
def subscribe(self, handler: Callable[[T], None]) -> None:
|
||||
"""Subscribe to stream."""
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Data Layer
|
||||
|
||||
### Repository Pattern
|
||||
|
||||
```python
|
||||
# infrastructure/repositories.py
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List, Optional, Generic, TypeVar
|
||||
|
||||
T = TypeVar('T')
|
||||
ID = TypeVar('ID')
|
||||
|
||||
class Repository(ABC, Generic[T, ID]):
|
||||
"""Base repository interface."""
|
||||
|
||||
@abstractmethod
|
||||
def get(self, id: ID) -> Optional[T]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_all(self) -> List[T]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def create(self, entity: T) -> T:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def update(self, entity: T) -> T:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def delete(self, id: ID) -> bool:
|
||||
pass
|
||||
|
||||
class SQLiteRepository(Repository[T, ID]):
|
||||
"""SQLite implementation."""
|
||||
|
||||
def __init__(self, db_path: str, entity_class: type):
|
||||
self.db_path = db_path
|
||||
self.entity_class = entity_class
|
||||
self._init_db()
|
||||
```
|
||||
|
||||
### Data Sync
|
||||
|
||||
```python
|
||||
# infrastructure/sync.py
|
||||
from typing import Dict, Any
|
||||
import aiohttp
|
||||
|
||||
class CloudSync:
|
||||
"""Sync data with cloud backend."""
|
||||
|
||||
def __init__(self, api_base: str, api_key: str):
|
||||
self.api_base = api_base
|
||||
self.api_key = api_key
|
||||
self._local_queue = []
|
||||
|
||||
async def sync_settings(self, settings: Dict[str, Any]) -> bool:
|
||||
"""Sync user settings to cloud."""
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(
|
||||
f"{self.api_base}/settings",
|
||||
json=settings,
|
||||
headers={"Authorization": f"Bearer {self.api_key}"}
|
||||
) as resp:
|
||||
return resp.status == 200
|
||||
|
||||
async def get_remote_settings(self) -> Dict[str, Any]:
|
||||
"""Get settings from cloud."""
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 API Gateway
|
||||
|
||||
### RESTful API
|
||||
|
||||
```python
|
||||
# api/gateway.py
|
||||
from fastapi import FastAPI, Depends, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from typing import List, Optional
|
||||
|
||||
app = FastAPI(
|
||||
title="EU-Utility API",
|
||||
version="3.0.0",
|
||||
description="Unified API for EU-Utility"
|
||||
)
|
||||
|
||||
# Authentication dependency
|
||||
async def verify_token(token: str = Header(...)):
|
||||
"""Verify API token."""
|
||||
# Implementation
|
||||
pass
|
||||
|
||||
@app.get("/plugins", response_model=List[PluginSchema])
|
||||
async def list_plugins(
|
||||
category: Optional[str] = None,
|
||||
active_only: bool = True,
|
||||
auth = Depends(verify_token)
|
||||
):
|
||||
"""List all plugins."""
|
||||
pass
|
||||
|
||||
@app.post("/plugins/{plugin_id}/activate")
|
||||
async def activate_plugin(
|
||||
plugin_id: str,
|
||||
auth = Depends(verify_token)
|
||||
):
|
||||
"""Activate a plugin."""
|
||||
pass
|
||||
|
||||
@app.get("/market/search")
|
||||
async def search_market(
|
||||
query: str,
|
||||
category: Optional[str] = None,
|
||||
limit: int = 20
|
||||
):
|
||||
"""Search market items."""
|
||||
pass
|
||||
|
||||
@app.get("/game/loot/recent")
|
||||
async def get_recent_loot(
|
||||
hours: int = 24,
|
||||
auth = Depends(verify_token)
|
||||
):
|
||||
"""Get recent loot events."""
|
||||
pass
|
||||
|
||||
@app.post("/ocr/recognize")
|
||||
async def ocr_recognize(
|
||||
region: tuple, # (x, y, width, height)
|
||||
auth = Depends(verify_token)
|
||||
):
|
||||
"""Recognize text from screen region."""
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📱 UI/UX Design
|
||||
|
||||
### Component Library
|
||||
|
||||
```python
|
||||
# ui/components.py
|
||||
from PyQt6.QtWidgets import *
|
||||
from PyQt6.QtCore import *
|
||||
from PyQt6.QtGui import *
|
||||
|
||||
class EUCard(QFrame):
|
||||
"""Card component for EU-Utility."""
|
||||
|
||||
def __init__(self, title: str, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setObjectName("EUCard")
|
||||
self.setup_ui(title)
|
||||
|
||||
def setup_ui(self, title: str):
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(16, 16, 16, 16)
|
||||
|
||||
# Header
|
||||
header = QLabel(title)
|
||||
header.setObjectName("card-title")
|
||||
layout.addWidget(header)
|
||||
|
||||
# Content area
|
||||
self.content = QFrame()
|
||||
self.content_layout = QVBoxLayout(self.content)
|
||||
layout.addWidget(self.content)
|
||||
|
||||
# Styling
|
||||
self.setStyleSheet("""
|
||||
#EUCard {
|
||||
background-color: #2d2d2d;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #3d3d3d;
|
||||
}
|
||||
#card-title {
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
""")
|
||||
|
||||
class EUButton(QPushButton):
|
||||
"""Primary button component."""
|
||||
|
||||
def __init__(self, text: str, variant: str = "primary"):
|
||||
super().__init__(text)
|
||||
self.variant = variant
|
||||
self.setup_style()
|
||||
|
||||
def setup_style(self):
|
||||
colors = {
|
||||
"primary": ("#6366f1", "#4f46e5"),
|
||||
"secondary": ("#3d3d3d", "#4d4d4d"),
|
||||
"danger": ("#ef4444", "#dc2626"),
|
||||
"success": ("#10b981", "#059669")
|
||||
}
|
||||
bg, hover = colors.get(self.variant, colors["primary"])
|
||||
|
||||
self.setStyleSheet(f"""
|
||||
EUButton {{
|
||||
background-color: {bg};
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 10px 20px;
|
||||
font-weight: 600;
|
||||
}}
|
||||
EUButton:hover {{
|
||||
background-color: {hover};
|
||||
}}
|
||||
""")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Implementation Roadmap
|
||||
|
||||
### Phase 1: Foundation (Week 1-2)
|
||||
- [ ] Set up new project structure
|
||||
- [ ] Implement domain entities
|
||||
- [ ] Create repository layer
|
||||
- [ ] Set up dependency injection
|
||||
|
||||
### Phase 2: Core Services (Week 3-4)
|
||||
- [ ] Event bus implementation
|
||||
- [ ] Plugin system v3.0
|
||||
- [ ] Security sandbox
|
||||
- [ ] Settings management
|
||||
|
||||
### Phase 3: API & UI (Week 5-6)
|
||||
- [ ] API Gateway
|
||||
- [ ] Component library
|
||||
- [ ] Main UI refactoring
|
||||
- [ ] Overlay system
|
||||
|
||||
### Phase 4: Integration (Week 7-8)
|
||||
- [ ] Plugin migration tool
|
||||
- [ ] Data migration
|
||||
- [ ] Testing suite
|
||||
- [ ] Documentation
|
||||
|
||||
### Phase 5: Release (Week 9)
|
||||
- [ ] Beta testing
|
||||
- [ ] Performance optimization
|
||||
- [ ] Security audit
|
||||
- [ ] Public release
|
||||
|
||||
---
|
||||
|
||||
## 📊 Migration Strategy
|
||||
|
||||
### From v2.x to v3.0
|
||||
|
||||
```python
|
||||
# migration/v2_to_v3.py
|
||||
class MigrationTool:
|
||||
"""Migrate plugins and data from v2 to v3."""
|
||||
|
||||
def migrate_plugin(self, v2_plugin_path: str) -> str:
|
||||
"""Convert v2 plugin to v3 format."""
|
||||
# Read v2 plugin
|
||||
# Generate v3 manifest
|
||||
# Update imports
|
||||
# Return new plugin path
|
||||
pass
|
||||
|
||||
def migrate_settings(self, v2_settings: dict) -> dict:
|
||||
"""Migrate user settings."""
|
||||
mapping = {
|
||||
"hotkeys.global_toggle": "hotkeys.overlay_toggle",
|
||||
"plugins.enabled": "plugins.active",
|
||||
# ...
|
||||
}
|
||||
# Apply mapping
|
||||
return migrated_settings
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Success Metrics
|
||||
|
||||
| Metric | Target |
|
||||
|--------|--------|
|
||||
| Startup Time | < 2 seconds |
|
||||
| Memory Usage | < 200MB |
|
||||
| Plugin Load | < 100ms each |
|
||||
| API Response | < 50ms |
|
||||
| Code Coverage | > 80% |
|
||||
| Plugin Compatibility | 100% v2 plugins work |
|
||||
|
||||
---
|
||||
|
||||
*Draft Application Design - EU-Utility v3.0*
|
||||
*Created: 2026-02-23*
|
||||
Loading…
Reference in New Issue