Skip to content

devslane/chat-react-module

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Chat Module

A fully modular, reusable chat package that can be integrated into any React application.

πŸ“¦ Installation

Install from the repository using a branch (replace main with your branch name):

# npm
npm install git+https://github.com/devslane/chat-react-module.git#main

# yarn
yarn add git+https://github.com/devslane/chat-react-module.git#main

# pnpm
pnpm add git+https://github.com/devslane/chat-react-module.git#main

Or add to package.json:

{
  "dependencies": {
    "@secondchair/chat-module": "github:devslane/chat-react-module#main"
  }
}

Ensure your bundler (Vite, Webpack, etc.) resolves TypeScript from node_modules or that you alias the package to source. The package exposes ./src/index.ts as the main entry.

πŸš€ Quick Start

The easiest way to integrate the chat module:

import { Chat } from "@secondchair/chat-module";
import "@secondchair/chat-module/index.css";

function App() {
  return (
    <Chat
      sessionId={123}
      onNavigateToSession={(newSessionId) => {
        router.push(`/chat/${newSessionId}`);
      }}
    />
  );
}

That's it! The Chat component includes everything you need.

🎯 Use Cases

1. Simple Chat (No Client/Context)

<Chat
  sessionId={sessionId}
  onNavigateToSession={(id) => router.push(`/chat/${id}`)}
/>

2. With Project/User Context (Instead of ClientId)

<Chat
  sessionId={sessionId}
  contextId={projectId}       // Generic context - can be projectId, userId, etc.
  contextType="project"
  contextName="My Project"
  onNavigateToSession={(id, contextId) =>
    router.push(`/projects/${contextId}/chat/${id}`)
  }
/>

3. Feature Toggles

<Chat
  sessionId={sessionId}
  config={{
    chatPanel: {
      enableToneSelector: false,      // Disable tone selector
      enableStateSelector: false,     // Disable state selector
      enableDocumentSelection: false, // Disable document selection
      enableFileAttachments: false,   // Disable file attachments
    },
    canvas: {
      enableSave: false,              // Disable save button
      enableExport: true,             // Keep export enabled
      exportFormats: {
        pdf: true,
        word: false,
        pleading: false,
        clipboard: true,
      },
    },
  }}
  onNavigateToSession={(id) => router.push(`/chat/${id}`)}
/>

4. Custom Adapters (Full Control)

<Chat
  sessionId={sessionId}
  adapters={{
    // Authentication
    auth: {
      getCurrentUser: () => currentUser,
      isAuthenticated: () => !!currentUser,
    },

    // Toast notifications
    toast: {
      success: (msg) => toast.success(msg),
      error: (msg) => toast.error(msg),
      info: (msg) => toast.info(msg),
      warning: (msg) => toast.warning(msg),
    },

    // Document management
    documents: {
      getDocumentCollections: () => [
        {
          type: "project",
          label: "Project Files",
          documents: projectDocuments,
        },
      ],
      onDocumentsSelected: (type, ids) => {
        console.log(`Selected ${ids.length} ${type} documents`);
      },
    },

    // Navigation
    navigation: {
      navigateToSession: (sessionId, contextId) => {
        router.push(`/chat/${sessionId}`);
      },
    },
  }}
  onNavigateToSession={(id) => router.push(`/chat/${id}`)}
/>

5. Event Tracking

<Chat
  sessionId={sessionId}
  onMessageSent={(message) => {
    analytics.track("message_sent", { messageId: message.id });
  }}
  onSessionCreated={(sessionId) => {
    analytics.track("session_created", { sessionId });
  }}
  onCanvasOpen={(messageId) => {
    analytics.track("canvas_opened", { messageId });
  }}
  onCanvasSave={(messageId, content) => {
    analytics.track("canvas_saved", { messageId });
  }}
  onNavigateToSession={(id) => router.push(`/chat/${id}`)}
/>

πŸ›  Advanced: Custom Layout

For full control over the layout, use the provider and individual components:

import {
  ChatModuleProvider,
  ChatPanel,
  Canvas,
  useCanvasManager,
} from "@secondchair/chat-module";

function MyCustomChat() {
  return (
    <ChatModuleProvider
      config={{ chatPanel: { enableCanvas: true } }}
      adapters={{
        toast: { success: toast.success, error: toast.error },
      }}
      context={{ contextId: projectId, contextType: "project" }}
    >
      <CustomLayout />
    </ChatModuleProvider>
  );
}

function CustomLayout() {
  const canvas = useCanvasManager();

  return (
    <div className="flex h-screen">
      <div className="flex-1">
        <ChatPanel
          chatSession={session}
          isCanvasOpen={canvas.isOpen}
          onOpenCanvas={(msg) => canvas.openCanvas(msg.id)}
        />
      </div>

      {canvas.isOpen && canvas.messageId && (
        <div className="w-1/2">
          <Canvas
            isOpen={canvas.isOpen}
            onClose={canvas.closeCanvas}
            messageId={canvas.messageId}
            selectedMessageIndex={canvas.selectedMessageIndex}
            lastIndex={canvas.totalMessages - 1}
            handleChangeCanvasMessage={(dir) =>
              dir > 0 ? canvas.goToNext() : canvas.goToPrevious()
            }
            isStreaming={canvas.isStreaming}
          />
        </div>
      )}
    </div>
  );
}

πŸ“‹ Available Hooks

Hook Purpose
useChatModule() Access all module context (includes config)
useFeatureEnabled(section, feature) Check if a feature is enabled
useMessageSubmission(options) Handle message sending
useDocuments(options) Manage document selection
useCanvasManager(options) Manage canvas state
useCurrentUser() Get current user from adapter
useChatToast() Access toast notifications

βš™οΈ Configuration Options

Canvas Features

canvas: {
  enableTextSelectionPopup: true,   // Text selection popup
  enableNavigation: true,           // Message navigation
  enableExport: true,               // Export/download
  enableSave: true,                 // Save functionality
  saveConfig: {
    showSaveButton: true,
    showSaveAndClose: true,
    enableAutoSave: false,
    autoSaveDelay: 2000,
  },
  exportFormats: {
    pdf: true,
    word: true,
    pleading: true,
    clipboard: true,
    showDownloadMenu: true,
  },
  toolbarFeatures: {
    undoRedo: true,
    fontSize: true,
    heading: true,
    textFormatting: true,
    colors: true,
    lists: true,
    alignment: true,
    insertElements: true,
    collapsible: true,
  },
}

Chat Panel Features

chatPanel: {
  enableDocumentSelection: true,    // Document selection
  enableFileAttachments: true,      // File uploads
  enableChatOptions: true,          // Chat options bar
  enableToneSelector: true,         // Tone selector
  enableStateSelector: true,        // State selector
  enableWelcomeSection: true,       // Welcome section
  enableMessageEditing: true,       // Edit messages
  enableCanvas: true,               // Canvas integration
  enableDocumentPreview: true,      // Document preview
  enableSecureDocumentLinks: true,  // Secure document links
}

Chat Features

chat: {
  enableStreaming: true,            // Streaming support
  enablePagination: true,           // Message pagination
  enableMessageReview: true,        // Message review/status
  enableComparisonMode: true,       // Comparison mode
  maxDocumentSelection: 10,         // Max documents
  maxClientDocumentSelection: 5,    // Max client documents
}

πŸ”Œ Adapter Types

interface ChatModuleAdapters {
  auth?: {
    getCurrentUser: () => ChatUser | null;
    isAuthenticated?: () => boolean;
  };

  documents?: {
    getDocumentCollections: () => DocumentCollection[];
    onDocumentsSelected?: (type: string, ids: (number | string)[]) => void;
    getSecureDocumentUrl?: (id: number | string) => Promise<string>;
    uploadDocument?: (file: File) => Promise<ChatDocument>;
  };

  navigation?: {
    navigateToSession: (sessionId: number, contextId?: number | string) => void;
    navigateToNewChat?: (contextId?: number | string) => void;
  };

  api?: {
    sendMessage: (payload: SendMessagePayload) => Promise<ChatMessage | void>;
    sendStreamingMessage?: (payload: SendMessagePayload) => void;
    fetchMessages?: (sessionId: number, page?: number) => Promise<ChatMessage[]>;
  };

  toast?: {
    success: (message: string) => void;
    error: (message: string) => void;
    info?: (message: string) => void;
    warning?: (message: string) => void;
  };

  storage?: {
    getItem: (key: string) => string | null;
    setItem: (key: string, value: string) => void;
    removeItem: (key: string) => void;
  };
}

πŸ“ Module Structure

chat-react-module/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ Chat.tsx           # All-in-one component
β”‚   β”‚   └── chat/
β”‚   β”‚       β”œβ”€β”€ chat-panel/    # Chat panel components
β”‚   β”‚       └── canvas/        # Canvas components
β”‚   β”œβ”€β”€ context/
β”‚   β”‚   └── ChatModuleProvider.tsx  # Main provider
β”‚   β”œβ”€β”€ hooks/
β”‚   β”‚   β”œβ”€β”€ useMessageSubmission.ts
β”‚   β”‚   β”œβ”€β”€ useDocuments.ts
β”‚   β”‚   └── useCanvasManager.ts
β”‚   β”œβ”€β”€ services/              # API services
β”‚   β”œβ”€β”€ types/                 # TypeScript types
β”‚   └── index.ts               # Main entry
β”œβ”€β”€ assets/                    # Icons and static assets
└── README.md

πŸ”„ Migration from ClientId

If you're migrating from the old clientId pattern:

// Before (with clientId)
<ChatPanel clientId={clientId} sessionId={sessionId} />

// After (generic context)
<Chat
  sessionId={sessionId}
  contextId={clientId}        // Same value, new name
  contextType="client"        // Identifies what kind of context
  onNavigateToSession={(id) => router.push(`/client/${clientId}/chat/${id}`)}
/>

🀝 Contributing

  1. All internal imports use @chatModule/* or relative paths.
  2. Keep components composable and independent.
  3. Run npm run typecheck before committing.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •