Plugin Architecture
Understanding how DebaterHub plugins work.
Overview
┌─────────────────────────────────────────────────────────────┐
│ Dashboard Shell │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Feed │ │ Write │ │ Detail │ Shells │
│ │ Shell │ │ Shell │ │ Shell │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ ┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐ │
│ │ JobCard │ │ AcademicEd │ │ JobDetail │ Plugin │
│ │ LibCard │ │ │ │ LibDetail │ Comps │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
Backend API
│
┌────────────┴────────────┐
│ │
PluginRepository Plugin Handlers
(Weaviate) (Python)
Key Concepts
1. Plugin Manifest
Every plugin starts with a manifest.ts that defines:
// apps/front/plugins/my-plugin/manifest.ts
import type { PluginManifest } from '@/lib/plugins/types';
export const manifest: PluginManifest = {
id: 'my-plugin',
name: 'My Plugin',
description: 'What my plugin does',
icon: 'Star',
version: '1.0.0',
author: 'Your Name',
// Navigation entry
nav: {
label: 'My Plugin',
icon: 'Star',
views: [
{ id: 'feed', label: 'Items', path: '/feed?plugin=my-plugin.feed' },
],
},
// Shell configurations
shells: {
feed: {
callable: 'list_items', // Backend handler to call
card: 'ItemCard', // Component to render each item
detail: 'ItemDetail', // Component for detail view
filters: ['status', 'date'],
},
},
// Dashboard widget (optional)
widget: {
component: 'MyWidget',
defaultSize: 'medium',
},
};
2. Shells
Shells are pre-built UI containers that host your components:
| Shell | Purpose | Components You Provide |
|---|---|---|
feed | List/grid of items | Card, Detail |
write | Editor view | Custom editor component |
detail | Full-page detail | Detail component |
The shell handles:
- Data fetching (calls your backend handler)
- Pagination
- Filtering
- Loading states
- Error handling
3. Backend Handlers
Handlers are Python functions that process data:
# packages/plugins/installed/my-plugin/handler.py
from services.weaviate.plugin_repository import PluginRepository
repo = PluginRepository()
def list_items(user_id: str, filters: dict = None) -> list:
"""Called by the feed shell to get items."""
return repo.list_by_user(
plugin_id="my-plugin",
user_id=user_id,
data_type="item",
filters=filters,
)
def get_item(user_id: str, item_id: str) -> dict:
"""Called by the detail shell to get a single item."""
return repo.get(
plugin_id="my-plugin",
item_id=item_id,
)
4. PluginData Storage
All plugin data is stored in a generic Weaviate collection:
# PluginData schema
{
"id": "uuid",
"plugin_id": "my-plugin",
"user_id": "user-uuid",
"data_type": "item", # Your custom type
"content": { # Your custom data
"title": "Example",
"description": "...",
},
"metadata": {
"created_at": "2024-01-01T00:00:00Z",
"status": "active",
},
"embedding": [...] # Auto-generated for search
}
File Structure
apps/front/plugins/my-plugin/
├── manifest.ts # Plugin definition
├── index.ts # Component exports
└── components/
├── ItemCard.tsx # Feed card component
├── ItemDetail.tsx # Detail view component
└── MyWidget.tsx # Dashboard widget
packages/plugins/installed/my-plugin/
├── __init__.py
├── handler.py # Backend handlers
└── models.py # Pydantic models (optional)
Component Props
Card Component
interface CardProps {
item: PluginData;
onClick: () => void;
}
export function ItemCard({ item, onClick }: CardProps) {
return (
<div onClick={onClick}>
<h3>{item.content.title}</h3>
<p>{item.content.description}</p>
</div>
);
}
Detail Component
interface DetailProps {
item: PluginData;
onClose: () => void;
onUpdate: (data: Partial<PluginData>) => void;
}
export function ItemDetail({ item, onClose, onUpdate }: DetailProps) {
// Full detail view with edit capabilities
}