9.3 KiB
🎨 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
{
"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
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
// 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
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
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
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
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)
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:
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)
{
"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
- Implement ExtensionManager in
src/js/extensions.js - Add hook points to existing code (app.js, ui.js, api.js)
- Create example extensions to validate API
- Build extension settings UI for enable/disable/configure
- Document extension development with starter template
🤝 Extension Development Workflow
# 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.