The Core Package provides a foundation for building custom chat interfaces with complete control over the UI and behavior. This guide shows you how to use the core package effectively.

Installation

# Using npm
npm install @opencx/widget

# Using pnpm
pnpm add @opencx/widget

# Using yarn
yarn add @opencx/widget

Basic Setup

Here’s a complete example of setting up a chat interface:

import { 
  createChat, 
  createConfig, 
  ApiCaller, 
  Platform,
  type MessageType,
  type BotMessageType 
} from '@opencx/widget';

// 1. Create configuration
const config = createConfig({
  token: 'YOUR_BOT_TOKEN',
  apiUrl: 'YOUR_API_URL',
  user: {
    email: "user@example.com",
    name: "User Name"
  }
});

// 2. Set up API caller
const api = new ApiCaller({
  config: config.getConfig()
});

// 3. Configure platform specifics
const platform: Platform = {
  env: {
    platform: 'web'  // or 'mobile', 'desktop'
  },
  storage: {
    getItem: async (key) => localStorage.getItem(key),
    setItem: async (key, value) => localStorage.setItem(key, value),
    removeItem: async (key) => localStorage.removeItem(key)
  },
  logger: {
    level: 'debug',
    prefix: '[OpenChat]',
    enabled: true
  }
};

// 4. Initialize chat
const chat = createChat({ api, config, platform });

State Management

The core package provides a robust state management system:

chat.chatState.subscribe((state) => {
  // Handle loading states
  if (state.loading.isLoading) {
    showLoadingIndicator();
    disableInput();
  } else {
    hideLoadingIndicator();
    enableInput();
  }

  // Handle errors
  if (state.error.hasError) {
    switch (state.error.code) {
      case 'SESSION_CREATION_FAILED':
        showConnectionError();
        break;
      case 'MESSAGE_SEND_FAILED':
        showMessageError(state.error.message);
        break;
    }
    return;
  }

  // Update messages
  state.messages.forEach((message: MessageType) => {
    if (message.type === 'FROM_USER') {
      renderUserMessage(message.content);
    } else if (message.type === 'FROM_BOT') {
      const botMessage = message as BotMessageType<{ text: string }>;
      renderBotMessage(botMessage.data.text);
    }
  });
});

Widget Initialization

The core package includes a prelude system for initializing the widget with organization data:

async function initializeWidget() {
  try {
    const prelude = await api.widgetPrelude();
    
    // Set organization info
    updateTitle(prelude.organizationName);
    
    // Handle initial questions
    if (prelude.initialQuestions?.length > 0) {
      renderSuggestions(prelude.initialQuestions);
    }
    
    // Set welcome message
    showWelcomeMessage();
  } catch (error) {
    handleInitializationError(error);
  }
}

Error Handling

Implement comprehensive error handling:

function handleChatError(error: any) {
  // Connection errors
  if (error.code === 'SESSION_CREATION_FAILED') {
    showError('Failed to connect to chat service', {
      canRetry: true,
      onRetry: initializeWidget
    });
  }
  
  // Message errors
  if (error.code === 'MESSAGE_SEND_FAILED') {
    showError(`Failed to send message: ${error.message}`, {
      canRetry: true,
      onRetry: () => resendMessage(error.messageId)
    });
  }
  
  // Generic errors
  showError('An unexpected error occurred', {
    duration: 5000,
    type: 'toast'
  });
}

Message Handling

Process different message types:

function handleMessage(message: MessageType) {
  switch (message.type) {
    case 'FROM_USER':
      return {
        element: createUserMessageElement(message.content),
        animation: 'slide-in-right'
      };
      
    case 'FROM_BOT':
      const botMessage = message as BotMessageType<{ text: string }>;
      return {
        element: createBotMessageElement(botMessage.data.text),
        animation: 'slide-in-left'
      };
      
    default:
      console.warn('Unknown message type:', message.type);
      return null;
  }
}

UI Integration Examples

Basic Chat Interface

// Message input handling
messageInput.addEventListener('keypress', (e) => {
  if (e.key === 'Enter' && !e.shiftKey) {
    e.preventDefault();
    sendMessage(messageInput.value);
    messageInput.value = '';
  }
});

// Send button handling
sendButton.addEventListener('click', () => {
  sendMessage(messageInput.value);
  messageInput.value = '';
});

// Message rendering
function renderMessage(message: MessageType) {
  const messageElement = document.createElement('div');
  messageElement.classList.add('message', 'animate__animated');
  
  if (message.type === 'FROM_USER') {
    messageElement.classList.add('user-message', 'animate__slideInRight');
  } else {
    messageElement.classList.add('bot-message', 'animate__slideInLeft');
  }
  
  messageElement.textContent = message.content;
  messagesContainer.appendChild(messageElement);
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
}

Suggestion Chips

function renderSuggestions(suggestions: string[]) {
  const container = document.createElement('div');
  container.classList.add('suggestions-container');
  
  suggestions.forEach((suggestion, index) => {
    const chip = document.createElement('button');
    chip.classList.add('suggestion-chip');
    chip.textContent = suggestion;
    
    // Add staggered animation
    setTimeout(() => {
      chip.classList.add('animate__fadeInUp');
    }, index * 100);
    
    chip.addEventListener('click', () => {
      sendMessage(suggestion);
    });
    
    container.appendChild(chip);
  });
  
  return container;
}

Best Practices

  1. State Management

    • Always subscribe to chat state changes
    • Handle all possible error states
    • Implement proper loading states
  2. Error Handling

    • Provide clear error messages
    • Implement retry mechanisms
    • Handle network issues gracefully
  3. UI/UX

    • Add loading indicators
    • Implement smooth animations
    • Provide clear feedback for user actions
  4. Performance

    • Clean up subscriptions when not needed
    • Implement proper error boundaries
    • Cache responses when appropriate