Add HTML/Frontend extensions architecture doc
This commit is contained in:
445
docs/HTML_EXTENSIONS.md
Normal file
445
docs/HTML_EXTENSIONS.md
Normal file
@@ -0,0 +1,445 @@
|
||||
# 🎨 HTML/Frontend Extensions Architecture
|
||||
|
||||
## Overview
|
||||
This document defines the **client-side extension system** for Chat Switchboard. Extensions are HTML/JS/CSS modules that can hook into the application lifecycle, add UI elements, intercept messages, and extend functionality.
|
||||
|
||||
---
|
||||
|
||||
## 🔌 Extension Interface
|
||||
|
||||
### Extension Manifest Structure
|
||||
```javascript
|
||||
{
|
||||
"id": "example-extension",
|
||||
"name": "Example Extension",
|
||||
"version": "1.0.0",
|
||||
"author": "Your Name",
|
||||
"description": "Does something cool",
|
||||
"type": "frontend", // or "hybrid" if it has backend components
|
||||
"permissions": ["ui", "messages", "storage"],
|
||||
"entrypoint": "extension.js",
|
||||
"styles": "extension.css",
|
||||
"hooks": {
|
||||
"onLoad": true,
|
||||
"onMessageSend": true,
|
||||
"onMessageReceive": true,
|
||||
"onModelSwitch": true
|
||||
},
|
||||
"ui": {
|
||||
"toolbar": true,
|
||||
"sidebar": false,
|
||||
"settings": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 Extension API
|
||||
|
||||
### Core Extension Class
|
||||
```javascript
|
||||
class ChatSwitchboardExtension {
|
||||
constructor(manifest) {
|
||||
this.manifest = manifest;
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
// Lifecycle hooks
|
||||
onLoad() {}
|
||||
onUnload() {}
|
||||
onEnable() {}
|
||||
onDisable() {}
|
||||
|
||||
// Message hooks
|
||||
async onMessageSend(message, context) {
|
||||
// Modify message before sending
|
||||
// return modified message or null to cancel
|
||||
return message;
|
||||
}
|
||||
|
||||
async onMessageReceive(message, context) {
|
||||
// Process received message
|
||||
// return modified message or original
|
||||
return message;
|
||||
}
|
||||
|
||||
async onMessageDisplay(message, element) {
|
||||
// Modify message DOM before display
|
||||
return element;
|
||||
}
|
||||
|
||||
// UI hooks
|
||||
onModelSwitch(oldModel, newModel) {}
|
||||
onChatSwitch(oldChatId, newChatId) {}
|
||||
onSettingsOpen() {}
|
||||
|
||||
// Render hooks
|
||||
renderToolbarButton() {
|
||||
// Return HTML string or DOM element
|
||||
return null;
|
||||
}
|
||||
|
||||
renderSidebarPanel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
renderSettingsPanel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
renderMessageAction(message) {
|
||||
// Add custom actions to message bubbles
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Extension Categories
|
||||
|
||||
### 1. **UI Extensions**
|
||||
Add visual elements and interface enhancements.
|
||||
|
||||
**Examples:**
|
||||
- Syntax highlighter themes
|
||||
- Message templates/snippets
|
||||
- Custom emoji pickers
|
||||
- Voice input buttons
|
||||
- Image/file attachments
|
||||
|
||||
**Hooks:** `renderToolbarButton`, `renderSidebarPanel`, `renderMessageAction`
|
||||
|
||||
---
|
||||
|
||||
### 2. **Message Processors**
|
||||
Transform messages before/after sending.
|
||||
|
||||
**Examples:**
|
||||
- Markdown preprocessor
|
||||
- Code formatter
|
||||
- Translation layer
|
||||
- Prompt templates
|
||||
- Token counter
|
||||
|
||||
**Hooks:** `onMessageSend`, `onMessageReceive`, `onMessageDisplay`
|
||||
|
||||
---
|
||||
|
||||
### 3. **Model Extensions**
|
||||
Enhance model selection and routing.
|
||||
|
||||
**Examples:**
|
||||
- Model performance stats
|
||||
- Cost calculator
|
||||
- Context window manager
|
||||
- Model recommendation engine
|
||||
- Fallback routing (if model fails, try another)
|
||||
|
||||
**Hooks:** `onModelSwitch`, access to `State.settings.model`
|
||||
|
||||
---
|
||||
|
||||
### 4. **Storage Extensions**
|
||||
Extend data persistence capabilities.
|
||||
|
||||
**Examples:**
|
||||
- Cloud sync (S3, Dropbox)
|
||||
- Export formats (PDF, DOCX)
|
||||
- Search indexing
|
||||
- Tagging system
|
||||
- Chat organization
|
||||
|
||||
**Permissions:** `storage`
|
||||
|
||||
---
|
||||
|
||||
### 5. **Tool/Function Extensions**
|
||||
Enable LLM function calling (critical for your use case).
|
||||
|
||||
**Examples:**
|
||||
- Web search tool
|
||||
- Calculator
|
||||
- Code execution sandbox
|
||||
- API caller
|
||||
- File system access
|
||||
|
||||
**Hooks:** `onFunctionCall`, `registerFunction`
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Extension Manager
|
||||
|
||||
### Core API
|
||||
```javascript
|
||||
// Global extension registry
|
||||
window.ExtensionManager = {
|
||||
extensions: new Map(),
|
||||
|
||||
// Register an extension
|
||||
register(manifest, ExtensionClass) {
|
||||
const ext = new ExtensionClass(manifest);
|
||||
this.extensions.set(manifest.id, ext);
|
||||
ext.onLoad();
|
||||
return ext;
|
||||
},
|
||||
|
||||
// Enable/disable
|
||||
enable(id) {},
|
||||
disable(id) {},
|
||||
unload(id) {},
|
||||
|
||||
// Hook execution
|
||||
async executeHook(hookName, ...args) {
|
||||
const results = [];
|
||||
for (const [id, ext] of this.extensions) {
|
||||
if (ext.enabled && ext[hookName]) {
|
||||
const result = await ext[hookName](...args);
|
||||
results.push({ id, result });
|
||||
}
|
||||
}
|
||||
return results;
|
||||
},
|
||||
|
||||
// Get all extensions with specific capability
|
||||
getByPermission(permission) {
|
||||
return Array.from(this.extensions.values())
|
||||
.filter(ext => ext.manifest.permissions.includes(permission));
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📂 Extension File Structure
|
||||
|
||||
```
|
||||
extensions/
|
||||
├── example-extension/
|
||||
│ ├── manifest.json
|
||||
│ ├── extension.js
|
||||
│ ├── extension.css (optional)
|
||||
│ ├── README.md
|
||||
│ └── assets/
|
||||
│ └── icon.svg
|
||||
```
|
||||
|
||||
### Loading Extensions
|
||||
```javascript
|
||||
async function loadExtension(path) {
|
||||
const manifest = await fetch(`${path}/manifest.json`).then(r => r.json());
|
||||
|
||||
// Load JS
|
||||
const module = await import(`${path}/${manifest.entrypoint}`);
|
||||
|
||||
// Load CSS if present
|
||||
if (manifest.styles) {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = `${path}/${manifest.styles}`;
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
// Register
|
||||
ExtensionManager.register(manifest, module.default);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Security & Permissions
|
||||
|
||||
### Permission System
|
||||
```javascript
|
||||
const PERMISSIONS = {
|
||||
ui: "Modify user interface",
|
||||
messages: "Read and modify messages",
|
||||
storage: "Access local storage",
|
||||
network: "Make network requests",
|
||||
settings: "Access user settings",
|
||||
clipboard: "Access clipboard",
|
||||
notifications: "Show notifications"
|
||||
};
|
||||
```
|
||||
|
||||
### Sandboxing (Future)
|
||||
- Use iframes for untrusted extensions
|
||||
- Content Security Policy restrictions
|
||||
- API key scoping (extensions can't access main API key)
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Example Extensions
|
||||
|
||||
### 1. Token Counter
|
||||
```javascript
|
||||
class TokenCounterExtension extends ChatSwitchboardExtension {
|
||||
onLoad() {
|
||||
this.tokenCount = 0;
|
||||
}
|
||||
|
||||
renderToolbarButton() {
|
||||
return `
|
||||
<button class="btn btn-small" id="tokenCounter">
|
||||
Tokens: <span id="tokenCount">0</span>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
async onMessageSend(message, context) {
|
||||
// Rough estimation: 1 token ≈ 4 chars
|
||||
this.tokenCount = Math.ceil(message.content.length / 4);
|
||||
document.getElementById('tokenCount').textContent = this.tokenCount;
|
||||
return message;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Code Formatter
|
||||
```javascript
|
||||
class CodeFormatterExtension extends ChatSwitchboardExtension {
|
||||
async onMessageDisplay(message, element) {
|
||||
if (message.role === 'assistant') {
|
||||
// Find code blocks and apply Prism.js highlighting
|
||||
element.querySelectorAll('pre code').forEach(block => {
|
||||
Prism.highlightElement(block);
|
||||
});
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
renderMessageAction(message) {
|
||||
if (message.content.includes('```')) {
|
||||
return `
|
||||
<button class="btn btn-small" onclick="copyAllCode()">
|
||||
📋 Copy All Code
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Model Router (Smart Fallback)
|
||||
```javascript
|
||||
class ModelRouterExtension extends ChatSwitchboardExtension {
|
||||
async onMessageSend(message, context) {
|
||||
// If message is long, use high-context model
|
||||
if (message.content.length > 5000) {
|
||||
const originalModel = State.settings.model;
|
||||
State.settings.model = 'claude-3-opus-20240229'; // High context
|
||||
|
||||
// Restore after response
|
||||
context.onComplete = () => {
|
||||
State.settings.model = originalModel;
|
||||
};
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Integration with App
|
||||
|
||||
### Modify `app.js` to support hooks:
|
||||
```javascript
|
||||
async function sendMessage() {
|
||||
// ... existing code ...
|
||||
|
||||
// Execute hook before sending
|
||||
const hookResults = await ExtensionManager.executeHook(
|
||||
'onMessageSend',
|
||||
message,
|
||||
{ chatId: State.currentChatId }
|
||||
);
|
||||
|
||||
// Check if any extension cancelled the send
|
||||
if (hookResults.some(r => r.result === null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply transformations
|
||||
for (const { result } of hookResults) {
|
||||
if (result && result !== message) {
|
||||
message = result;
|
||||
}
|
||||
}
|
||||
|
||||
// ... continue with API call ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Extension Discovery
|
||||
|
||||
### Extension Store (Future)
|
||||
```javascript
|
||||
{
|
||||
"extensions": [
|
||||
{
|
||||
"id": "web-search",
|
||||
"name": "Web Search Tool",
|
||||
"description": "Adds web search capability via function calling",
|
||||
"author": "Chat Switchboard Team",
|
||||
"version": "1.0.0",
|
||||
"downloadUrl": "https://extensions.switchboard/web-search.zip",
|
||||
"verified": true,
|
||||
"rating": 4.8,
|
||||
"downloads": 1250
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Next Steps
|
||||
|
||||
1. **Implement ExtensionManager** in `src/js/extensions.js`
|
||||
2. **Add hook points** to existing code (app.js, ui.js, api.js)
|
||||
3. **Create example extensions** to validate API
|
||||
4. **Build extension settings UI** for enable/disable/configure
|
||||
5. **Document extension development** with starter template
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Extension Development Workflow
|
||||
|
||||
```bash
|
||||
# Create new extension
|
||||
mkdir extensions/my-extension
|
||||
cd extensions/my-extension
|
||||
|
||||
# Generate manifest
|
||||
cat > manifest.json << EOF
|
||||
{
|
||||
"id": "my-extension",
|
||||
"name": "My Extension",
|
||||
"version": "1.0.0",
|
||||
"entrypoint": "extension.js",
|
||||
"permissions": ["messages"]
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create extension class
|
||||
cat > extension.js << EOF
|
||||
class MyExtension extends ChatSwitchboardExtension {
|
||||
onLoad() {
|
||||
console.log('My extension loaded!');
|
||||
}
|
||||
}
|
||||
export default MyExtension;
|
||||
EOF
|
||||
|
||||
# Test in dev mode
|
||||
# Extensions auto-load from /extensions/ directory
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**This architecture makes Chat Switchboard infinitely extensible while keeping the core lean.**
|
||||
Reference in New Issue
Block a user