Advanced Usage

Unlock the full potential of @termix-it/react-tool with advanced features like function calling, smart contract integration, streaming mode, floating chat widgets, custom implementations, and comprehensive API integration.

Function Calling

The ChatInterface automatically handles different types of function calls based on your AI configuration.

REST API Function Calls

Configure your AI to make REST API calls that are automatically executed:
import { useState } from 'react';
import { ChatInterface, FunctionCall, ExecutionResult } from '@termix-it/react-tool';

function APIIntegratedChat() {
  const apiHeaders = {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  };

  const handleFunctionExecution = (call: FunctionCall, result: ExecutionResult) => {
    if (call.metadata?.type === 'api') {
      console.log(`API call to ${call.metadata.path}:`, result.data);
      
      if (result.success) {
        // Handle successful API response
        if (call.name === 'getUserData') {
          // Update user state or show notification
        }
      } else {
        // Handle API errors
        console.error('API call failed:', result.error);
      }
    }
  };

  return (
    <ChatInterface
      projectId="your-project-id"
      aiConfigId="your-ai-config-id"
      authorization="Bearer your-token"
      restExecuteHeader={apiHeaders}
      onFunctionExecuted={handleFunctionExecution}
      placeholder="I can help you access user data, update settings, and more!"
    />
  );
}

Dynamic Header Management

Update API headers dynamically based on user state:
function DynamicHeadersChat() {
  const [userToken, setUserToken] = useState('');
  const [customHeaders, setCustomHeaders] = useState<Record<string, string>>({});

  useEffect(() => {
    // Update headers when user token changes
    if (userToken) {
      setCustomHeaders({
        'Authorization': `Bearer ${userToken}`,
        'X-User-ID': 'current-user-id',
        'X-Timestamp': Date.now().toString()
      });
    }
  }, [userToken]);

  return (
    <div>
      <input
        type="text"
        placeholder="Enter your API token"
        value={userToken}
        onChange={(e) => setUserToken(e.target.value)}
        className="mb-4 p-2 border rounded"
      />
      
      <ChatInterface
        projectId="your-project-id"
        aiConfigId="your-ai-config-id"
        authorization={`Bearer ${userToken}`}
        restExecuteHeader={customHeaders}
        placeholder="Ask me to fetch your data or update settings..."
      />
    </div>
  );
}

ChatWidget Advanced Usage

The ChatWidget component provides sophisticated floating chat capabilities with extensive customization options.

Dynamic Widget State Management

Control widget behavior based on application state and user interactions:
import { useState, useEffect, useCallback } from 'react';
import { ChatWidget, ChatInterface, Message, FunctionCall, ExecutionResult } from '@termix-it/react-tool';

function DynamicWidget() {
  const [isOpen, setIsOpen] = useState(false);
  const [unreadCount, setUnreadCount] = useState(0);
  const [currentPage, setCurrentPage] = useState('/dashboard');
  const { user, isAuthenticated } = useAuth();

  // Auto-open widget for new users
  useEffect(() => {
    if (isAuthenticated && user.isFirstTimeUser) {
      setIsOpen(true);
    }
  }, [isAuthenticated, user]);

  // Page-specific context
  const getContextualWelcome = useCallback(() => {
    const contexts = {
      '/dashboard': 'Need help with your dashboard? I can explain any metrics or features.',
      '/settings': 'Having trouble with settings? I can guide you through configuration.',
      '/billing': 'Questions about billing or subscription? I\'m here to help.',
      default: 'Hi! How can I assist you today?'
    };
    return contexts[currentPage] || contexts.default;
  }, [currentPage]);

  const handleNewResponse = useCallback((message: Message) => {
    if (!isOpen) {
      setUnreadCount(prev => prev + 1);
    }
  }, [isOpen]);

  const handleOpenChange = useCallback((open: boolean) => {
    setIsOpen(open);
    if (open) {
      setUnreadCount(0); // Clear notifications when opened
    }
  }, []);

  if (!isAuthenticated) {
    return (
      <div className="fixed bottom-6 right-6">
        <ChatWidget
          title="Guest Support"
          buttonClassName="opacity-75"
        >
          <div className="p-4 text-center">
            <p className="mb-4">Please sign in to access AI assistance</p>
            <button className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
              Sign In
            </button>
          </div>
        </ChatWidget>
      </div>
    );
  }

  return (
    <div className="fixed bottom-6 right-6">
      {unreadCount > 0 && (
        <div className="absolute -top-2 -left-2 bg-red-500 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs z-10">
          {unreadCount}
        </div>
      )}
      
      <ChatWidget
        title={currentPage === '/dashboard' ? `Hi ${user.name}! - Dashboard Help` : `Hi ${user.name}!`}
        defaultOpen={isOpen}
        onOpenChange={handleOpenChange}
        buttonClassName={unreadCount > 0 ? 'animate-pulse ring-2 ring-red-400' : ''}
      >
        <ChatInterface
          projectId="your-project-id"
          aiConfigId="your-ai-config-id"
          authorization={`Bearer ${user.token}`}
          enableStreamingMode={true}
          placeholder={getContextualWelcome()}
          onResponseReceived={handleNewResponse}
          apiHeaders={{
            'X-User-ID': user.id,
            'X-Current-Page': currentPage,
            'X-User-Role': user.role
          }}
          className="h-full"
        />
      </ChatWidget>
    </div>
  );
}

Multi-Widget Implementation

Deploy multiple specialized widgets for different use cases:
function MultiWidgetApp() {
  const [activeWidgets, setActiveWidgets] = useState({
    support: false,
    sales: false,
    technical: false
  });

  return (
    <div className="relative min-h-screen">
      {/* Main content */}
      <div>Your application content</div>

      {/* Support Widget - Bottom Right */}
      <div className="fixed bottom-6 right-6">
        <ChatWidget
          title="Customer Support"
          buttonIcon="/icons/support.svg"
          defaultOpen={activeWidgets.support}
          onOpenChange={(open) => setActiveWidgets(prev => ({...prev, support: open}))}
        >
          <ChatInterface
            projectId="support-project-id"
            aiConfigId="support-ai-config"
            authorization="Bearer support-token"
            enableStreamingMode={true}
            placeholder="Hi! I'm your support assistant. How can I help?"
          />
        </ChatWidget>
      </div>

      {/* Sales Widget - Bottom Left */}
      <div className="fixed bottom-6 left-6">
        <ChatWidget
          title="Sales Assistant"
          buttonIcon="/icons/sales.svg"
          buttonClassName="bg-green-500 hover:bg-green-600"
          defaultOpen={activeWidgets.sales}
          onOpenChange={(open) => setActiveWidgets(prev => ({...prev, sales: open}))}
        >
          <ChatInterface
            projectId="sales-project-id"
            aiConfigId="sales-ai-config"
            authorization="Bearer sales-token"
            enableStreamingMode={true}
            placeholder="Interested in our products? I can help with pricing and features!"
          />
        </ChatWidget>
      </div>

      {/* Technical Widget - Top Right */}
      <div className="fixed top-6 right-6">
        <ChatWidget
          title="Technical Support"
          buttonIcon="/icons/technical.svg"
          buttonClassName="bg-purple-500 hover:bg-purple-600"
          defaultOpen={activeWidgets.technical}
          onOpenChange={(open) => setActiveWidgets(prev => ({...prev, technical: open}))}
        >
          <ChatInterface
            projectId="technical-project-id"
            aiConfigId="technical-ai-config"
            authorization="Bearer technical-token"
            enableStreamingMode={true}
            placeholder="Need technical help? I can assist with APIs, integrations, and troubleshooting."
          />
        </ChatWidget>
      </div>
    </div>
  );
}

Real-time Streaming Implementation

Advanced streaming configurations for optimal user experience:

Streaming with Connection Monitoring

import { useState, useCallback, useRef, useEffect } from 'react';

function MonitoredStreamingChat() {
  const [connectionStatus, setConnectionStatus] = useState<'disconnected' | 'connecting' | 'connected' | 'error'>('disconnected');
  const [streamingStats, setStreamingStats] = useState({
    charactersReceived: 0,
    chunksReceived: 0,
    averageLatency: 0
  });
  const streamStartTimeRef = useRef<number>(0);

  const handleMessageSent = useCallback(() => {
    setConnectionStatus('connecting');
    streamStartTimeRef.current = Date.now();
    setStreamingStats(prev => ({ ...prev, charactersReceived: 0, chunksReceived: 0 }));
  }, []);

  const handleResponseReceived = useCallback((message: Message) => {
    setConnectionStatus('connected');
    const streamEndTime = Date.now();
    const latency = streamEndTime - streamStartTimeRef.current;
    
    setStreamingStats(prev => ({
      ...prev,
      charactersReceived: message.content.length,
      averageLatency: latency
    }));
  }, []);

  const handleError = useCallback((error: any) => {
    setConnectionStatus('error');
    console.error('Streaming error:', error);
  }, []);

  return (
    <div className="space-y-4">
      {/* Connection Status Dashboard */}
      <div className="p-4 border rounded-lg bg-gray-50">
        <h3 className="text-sm font-semibold mb-2">Streaming Status</h3>
        <div className="grid grid-cols-2 gap-4 text-xs">
          <div>
            <span className="text-gray-600">Status:</span>
            <span className={`ml-2 font-semibold ${
              connectionStatus === 'connected' ? 'text-green-600' :
              connectionStatus === 'connecting' ? 'text-yellow-600' :
              connectionStatus === 'error' ? 'text-red-600' :
              'text-gray-500'
            }`}>
              {connectionStatus}
            </span>
          </div>
          <div>
            <span className="text-gray-600">Characters:</span>
            <span className="ml-2 font-mono">{streamingStats.charactersReceived}</span>
          </div>
          <div>
            <span className="text-gray-600">Chunks:</span>
            <span className="ml-2 font-mono">{streamingStats.chunksReceived}</span>
          </div>
          <div>
            <span className="text-gray-600">Latency:</span>
            <span className="ml-2 font-mono">{streamingStats.averageLatency}ms</span>
          </div>
        </div>
      </div>

      <ChatInterface
        projectId="your-project-id"
        aiConfigId="your-ai-config-id"
        authorization="Bearer your-token"
        enableStreamingMode={true}
        onMessageSent={handleMessageSent}
        onResponseReceived={handleResponseReceived}
        onError={handleError}
        placeholder="Streaming mode active! Watch the real-time stats above."
      />
    </div>
  );
}

Streaming with Progressive Enhancement

function AdaptiveStreamingChat() {
  const [streamingSupported, setStreamingSupported] = useState(true);
  const [streamingEnabled, setStreamingEnabled] = useState(true);
  const [retryCount, setRetryCount] = useState(0);
  const maxRetries = 3;

  const handleError = useCallback((error: any) => {
    console.error('Streaming error:', error);
    
    // Check if it's a streaming-specific error
    if (error.message?.includes('EventSource') || error.message?.includes('text/event-stream')) {
      setRetryCount(prev => prev + 1);
      
      if (retryCount >= maxRetries) {
        console.warn('Streaming failed after retries, falling back to regular mode');
        setStreamingEnabled(false);
        setStreamingSupported(false);
      }
    }
  }, [retryCount]);

  const toggleStreaming = useCallback(() => {
    setStreamingEnabled(prev => !prev);
    setRetryCount(0); // Reset retry count when manually toggling
  }, []);

  return (
    <div className="space-y-4">
      {/* Streaming Controls */}
      <div className="flex items-center justify-between p-3 border rounded bg-blue-50">
        <div className="flex items-center space-x-2">
          <span className="text-sm font-medium">Streaming Mode:</span>
          <button
            onClick={toggleStreaming}
            disabled={!streamingSupported}
            className={`px-3 py-1 text-xs rounded font-medium ${
              streamingEnabled && streamingSupported
                ? 'bg-green-100 text-green-800 border border-green-300'
                : 'bg-gray-100 text-gray-600 border border-gray-300'
            } ${!streamingSupported ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
          >
            {streamingEnabled && streamingSupported ? 'ON' : 'OFF'}
          </button>
        </div>
        
        {!streamingSupported && (
          <span className="text-xs text-orange-600">
            Fallback to regular mode
          </span>
        )}
        
        {retryCount > 0 && streamingSupported && (
          <span className="text-xs text-yellow-600">
            Retry {retryCount}/{maxRetries}
          </span>
        )}
      </div>

      <ChatInterface
        projectId="your-project-id"
        aiConfigId="your-ai-config-id"
        authorization="Bearer your-token"
        enableStreamingMode={streamingEnabled && streamingSupported}
        onError={handleError}
        placeholder={
          streamingEnabled && streamingSupported
            ? "Streaming mode enabled - responses will appear in real-time!"
            : "Regular mode active - responses will appear all at once."
        }
      />
    </div>
  );
}

Smart Contract Integration

Enable blockchain interactions with automatic smart contract execution:

Basic Smart Contract Setup

import { ChatInterface, FunctionCall, ExecutionResult } from '@termix-it/react-tool';
import { useWeb3 } from './hooks/useWeb3'; // Your web3 hook

function DeFiChat() {
  const { account, connect, isConnected } = useWeb3();

  const handleContractExecution = (call: FunctionCall, result: ExecutionResult) => {
    if (call.metadata?.type === 'contract') {
      const { contractName, contractAddress, chainName } = call.metadata;
      
      console.log(`Contract call on ${chainName}:`, {
        contract: contractName,
        address: contractAddress,
        function: call.name,
        parameters: call.parameters
      });

      if (result.success && result.data?.transactionHash) {
        // Show transaction confirmation
        showTransactionNotification(result.data.transactionHash);
        
        // Update UI state based on contract interaction
        if (call.name === 'approve') {
          // Handle approval transaction
        } else if (call.name === 'transfer') {
          // Handle transfer transaction
        }
      }
    }
  };

  const showTransactionNotification = (txHash: string) => {
    // Show success notification with etherscan link
    console.log(`Transaction submitted: https://etherscan.io/tx/${txHash}`);
  };

  return (
    <div>
      {!isConnected && (
        <button 
          onClick={connect}
          className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
        >
          Connect Wallet
        </button>
      )}
      
      {isConnected && (
        <div className="mb-4 p-3 bg-green-100 border border-green-400 rounded">
          Connected: {account}
        </div>
      )}
      
      <ChatInterface
        projectId="your-project-id"
        aiConfigId="defi-config-id"
        authorization="Bearer your-token"
        onFunctionExecuted={handleContractExecution}
        placeholder={
          isConnected 
            ? "I can help you interact with smart contracts, check balances, and execute transactions!"
            : "Please connect your wallet to enable smart contract interactions."
        }
      />
    </div>
  );
}

Multi-Chain Contract Support

Handle contracts across different blockchain networks:
function MultiChainDeFiChat() {
  const [supportedChains] = useState([
    { name: 'ethereum', rpc: 'https://mainnet.infura.io/v3/...' },
    { name: 'polygon', rpc: 'https://polygon-rpc.com' },
    { name: 'arbitrum', rpc: 'https://arb1.arbitrum.io/rpc' }
  ]);

  const handleContractExecution = (call: FunctionCall, result: ExecutionResult) => {
    if (call.metadata?.type === 'contract') {
      const chainName = call.metadata.chainName;
      const chain = supportedChains.find(c => c.name === chainName);
      
      if (result.success) {
        console.log(`Transaction on ${chainName}:`, result.data?.transactionHash);
        
        // Chain-specific handling
        switch (chainName) {
          case 'ethereum':
            // Handle Ethereum transactions
            break;
          case 'polygon':
            // Handle Polygon transactions
            break;
          case 'arbitrum':
            // Handle Arbitrum transactions
            break;
        }
      }
    }
  };

  return (
    <ChatInterface
      projectId="your-project-id"
      aiConfigId="multi-chain-config-id"
      authorization="Bearer your-token"
      onFunctionExecuted={handleContractExecution}
      placeholder="I can help you interact with contracts on Ethereum, Polygon, and Arbitrum!"
    />
  );
}

Knowledge Base Integration

Set up sophisticated knowledge base search and reference systems:

Custom Knowledge Base Handling

function DocumentationAssistant() {
  const [knowledgeStats, setKnowledgeStats] = useState({
    searchCount: 0,
    documentsFound: 0
  });

  const handleResponse = (message: Message) => {
    if (message.knowledgeContext && message.knowledgeContext.length > 0) {
      setKnowledgeStats(prev => ({
        searchCount: prev.searchCount + 1,
        documentsFound: prev.documentsFound + message.knowledgeContext!.length
      }));
    }
  };

  return (
    <div>
      <div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded">
        <p className="text-sm">
          Knowledge Base Stats: {knowledgeStats.searchCount} searches, 
          {knowledgeStats.documentsFound} documents referenced
        </p>
      </div>
      
      <ChatInterface
        projectId="your-project-id"
        aiConfigId="docs-assistant-config-id"
        authorization="Bearer your-token"
        maxKnowledgeResults={5}
        onResponseReceived={handleResponse}
        placeholder="Ask me about your docs, APIs, or code examples..."
      />
    </div>
  );
}

Custom Theming

Implement advanced theming with CSS custom properties:

Dark Mode Theme

custom-theme.css
/* Dark theme */
[data-theme="dark"] {
  --primary: 210 100% 60%;
  --primary-foreground: 0 0% 100%;
  --secondary: 215 25% 15%;
  --secondary-foreground: 210 40% 90%;
  --accent: 215 25% 20%;
  --accent-foreground: 210 40% 90%;
  --destructive: 0 75% 60%;
  --destructive-foreground: 0 0% 100%;
  --border: 215 25% 25%;
  --input: 215 25% 20%;
  --ring: 210 100% 60%;
  --background: 215 30% 8%;
  --foreground: 210 40% 98%;
}

/* Custom chat styling */
.custom-chat {
  --chat-bg: var(--background);
  --message-user-bg: var(--primary);
  --message-ai-bg: var(--secondary);
  --input-bg: var(--input);
}
function ThemedChat() {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');

  useEffect(() => {
    document.documentElement.setAttribute('data-theme', theme);
  }, [theme]);

  return (
    <div>
      <button 
        onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
        className="mb-4 px-4 py-2 border rounded"
      >
        Switch to {theme === 'light' ? 'dark' : 'light'} mode
      </button>
      
      <ChatInterface
        projectId="your-project-id"
        aiConfigId="your-ai-config-id"
        authorization="Bearer your-token"
        className="custom-chat"
      />
    </div>
  );
}

ReAct Pattern Implementation

Enable sophisticated reasoning and acting patterns:
function ReActEnabledChat() {
  const [reasoningSteps, setReasoningSteps] = useState<string[]>([]);

  const handleResponse = (message: Message) => {
    // Extract reasoning steps from ReAct pattern responses
    const reasoning = extractReasoningSteps(message.content);
    if (reasoning.length > 0) {
      setReasoningSteps(prev => [...prev, ...reasoning]);
    }
  };

  const extractReasoningSteps = (content: string): string[] => {
    const reasoningMatch = content.match(/Thought: (.+?)(?=\nAction:|$)/g);
    return reasoningMatch ? reasoningMatch.map(step => step.replace('Thought: ', '')) : [];
  };

  return (
    <div>
      {reasoningSteps.length > 0 && (
        <div className="mb-4 p-3 bg-yellow-50 border border-yellow-200 rounded">
          <h3 className="font-semibold mb-2">AI Reasoning Steps:</h3>
          <ul className="text-sm space-y-1">
            {reasoningSteps.slice(-3).map((step, index) => (
              <li key={index} className="flex items-start">
                <span className="mr-2">🤔</span>
                {step}
              </li>
            ))}
          </ul>
        </div>
      )}
      
      <ChatInterface
        projectId="your-project-id"
        aiConfigId="react-pattern-config-id"
        authorization="Bearer your-token"
        onResponseReceived={handleResponse}
        placeholder="I'll show you my reasoning process as I work through complex problems!"
      />
    </div>
  );
}

Error Handling and Recovery

Implement robust error handling:
function RobustChat() {
  const [errors, setErrors] = useState<string[]>([]);
  const [isRetrying, setIsRetrying] = useState(false);

  const handleError = (error: any) => {
    const errorMessage = error.message || 'An unknown error occurred';
    setErrors(prev => [...prev.slice(-2), errorMessage]); // Keep last 3 errors
    
    // Implement retry logic for specific errors
    if (error.code === 'NETWORK_ERROR' && !isRetrying) {
      setIsRetrying(true);
      setTimeout(() => {
        setIsRetrying(false);
        // Trigger retry mechanism
      }, 2000);
    }
  };

  const clearErrors = () => setErrors([]);

  return (
    <div>
      {errors.length > 0 && (
        <div className="mb-4 p-3 bg-red-50 border border-red-200 rounded">
          <div className="flex justify-between items-start">
            <div>
              <h3 className="font-semibold text-red-800">Recent Errors:</h3>
              <ul className="text-sm text-red-700 mt-1">
                {errors.map((error, index) => (
                  <li key={index}>{error}</li>
                ))}
              </ul>
            </div>
            <button 
              onClick={clearErrors}
              className="text-red-600 hover:text-red-800 text-sm"
            >
              Clear
            </button>
          </div>
        </div>
      )}
      
      {isRetrying && (
        <div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded">
          <p className="text-blue-700">Retrying connection...</p>
        </div>
      )}
      
      <ChatInterface
        projectId="your-project-id"
        aiConfigId="your-ai-config-id"
        authorization="Bearer your-token"
        onError={handleError}
      />
    </div>
  );
}

Performance Optimization

Optimize performance for production use:
import { memo, useMemo, useCallback } from 'react';

const OptimizedChat = memo(function OptimizedChat({ 
  projectId, 
  aiConfigId, 
  userToken 
}: {
  projectId: string;
  aiConfigId: string;
  userToken: string;
}) {
  // Memoize expensive calculations
  const apiHeaders = useMemo(() => ({
    'X-Client-Version': '1.0.0',
    'X-Session-ID': `session-${Date.now()}`
  }), []);

  // Memoize callback functions
  const handleFunctionExecution = useCallback((call: FunctionCall, result: ExecutionResult) => {
    console.log('Function executed:', call.name, result.success);
  }, []);

  const handleError = useCallback((error: any) => {
    console.error('Chat error:', error);
  }, []);

  return (
    <ChatInterface
      projectId={projectId}
      aiConfigId={aiConfigId}
      authorization={`Bearer ${userToken}`}
      apiHeaders={apiHeaders}
      onFunctionExecuted={handleFunctionExecution}
      onError={handleError}
      // Disable expensive features in production if not needed
      showUsageInfo={false}
      maxKnowledgeResults={3}
    />
  );
});

export default OptimizedChat;

API Integration

Default API Configuration

The SDK now automatically connects to the Termix API:
  • Production: https://dashboard.termix.ai
  • Local Development: http://127.0.0.1:3001 (when using npm run dev:local)
<ChatInterface
  projectId="your-project-id"
  aiConfigId="your-ai-config-id"
  authorization="Bearer your-token"
  enableStreamingMode={true}
  // ... other props
/>

Development Setup

For local development with a local backend server:
  1. Install the SDK in your project:
    npm install @termix-it/react-tool
    
  2. For local backend development: If you need to connect to a local backend server running at http://127.0.0.1:3001, you have two options: Option 1: Build the SDK locally with local configuration
    # In the react-tool directory
    npm run dev:local
    
    # Then link it to your project
    npm link
    
    # In your project directory
    npm link @termix-it/react-tool
    
    Option 2: Use environment variable (if your build system supports it)
    # Set the environment variable before building
    REACT_APP_API_LOCAL=true npm run build
    

API Endpoints

The SDK automatically connects to these Termix API endpoints:

Chat Endpoints

  • POST /api/v1/ai/projects/{projectId}/chat - Regular chat messages
  • POST /api/v1/ai/projects/{projectId}/chat/stream - Streaming chat messages (SSE)
  • GET /api/v1/ai/projects/{projectId}/configs/{configId} - Get AI config (including greeting message)

Function Execution

  • REST API endpoints for function calls (auto-detected)
  • Smart contract execution via web3 providers

Knowledge Base

  • GET /api/v1/projects/{projectId}/knowledge-base/search - Search knowledge base
  • GET /api/v1/knowledge/{projectId} - Get all knowledge

MCP Tools

  • GET /api/v1/mcp-tools/{projectId}/parsed-endpoints - Get parsed API endpoints
  • GET /api/v1/mcp-tools/{projectId}/parsed-contracts - Get parsed smart contracts

Styling and Customization

Style Import

Styles are now automatically bundled with the components - no separate CSS import needed:
import { ChatInterface } from '@termix-it/react-tool';
// No need to import CSS - styles are now bundled with components
All styles are scoped with .termix- prefix to prevent conflicts.

Using Inline Styles

Both ChatInterface and ChatWidget support inline styles via the style prop:
// ChatInterface with custom styles
<ChatInterface
  projectId="your-project-id"
  aiConfigId="your-ai-config-id"
  style={{ 
    height: '600px', 
    borderRadius: '12px',
    boxShadow: '0 4px 6px rgba(0,0,0,0.1)'
  }}
  messagesStyle={{ 
    backgroundColor: '#f9f9f9' 
  }}
  inputStyle={{ 
    fontSize: '16px',
    padding: '12px 16px'
  }}
/>

// ChatWidget with custom button and dialog styles
<ChatWidget
  style={{ position: 'fixed', bottom: '20px', right: '20px' }}
  buttonStyle={{ 
    width: '60px', 
    height: '60px',
    backgroundColor: '#6366f1'
  }}
  dialogStyle={{ 
    width: '400px',
    height: '500px',
    borderRadius: '16px'
  }}
>
  <ChatInterface {...props} />
</ChatWidget>

Custom Theming with CSS Variables

You can override the default colors using CSS custom properties:
:root {
  --primary: 220 100% 50%;
  --primary-foreground: 0 0% 100%;
  --secondary: 210 40% 96%;
  --secondary-foreground: 222.2 84% 4.9%;
  --accent: 210 40% 96%;
  --accent-foreground: 222.2 84% 4.9%;
  --destructive: 0 84.2% 60.2%;
  --destructive-foreground: 210 40% 98%;
  --border: 214.3 31.8% 91.4%;
  --input: 214.3 31.8% 91.4%;
  --ring: 222.2 84% 4.9%;
}

Exported Components and Utilities

// Main components
export { ChatInterface } from './components/ChatInterface';
export { ChatWidget } from './components/ChatWidget';
export { FunctionCallDisplay } from './components/FunctionCallDisplay';

// Services
export { APIService } from './services/api';
export { FunctionCallExecutor } from './services/functionCallExecutor';
export { ToolCallProcessor } from './services/toolCallProcessor';

// Utilities
export { cn, extractFunctionCalls } from './lib/utils';

// Hooks
export { useToast } from './hooks/useToast';

Troubleshooting

Styles Not Loading Correctly

You must manually import the styles. If you experience issues:
  1. Check for CSS conflicts: The library’s styles are scoped to .termix-container to avoid conflicts, but ensure your global styles don’t override them.
  2. Tailwind CSS conflicts: If your project uses Tailwind CSS, the scoped styles should prevent conflicts. The components also use inline styles for critical styling.
  3. Header visibility: The chat header uses inline styles to ensure proper display:
    • Blue gradient background: linear-gradient(to right, #3b82f6, #2563eb)
    • White text color is explicitly set

Input Alignment Issues

The input area automatically aligns the text input and send button vertically. If you experience alignment issues, check that parent containers don’t override the flex layout.

Best Practices Summary