This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/docs/HTML_EXTENSIONS.md

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

  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

# 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.