xtablo-source/docs/superpowers/plans/2026-04-12-chatcn-integration.md
Arthur Belleville 517526ef99
docs: add chatcn integration design spec and implementation plan
Spec and plan for integrating chatcn as @xtablo/chat-ui package,
replacing chatscope for the chat UI rendering layer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 12:06:52 +02:00

29 KiB

chatcn Integration as @xtablo/chat-ui Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Replace chatscope with chatcn as a new @xtablo/chat-ui workspace package, adapting theming to xtablo's design tokens.

Architecture: Copy chatcn's 7 source files into packages/chat-ui/src/, remap imports to use @xtablo/shared and @xtablo/ui, replace all var(--chat-*) CSS variables with Tailwind utility classes mapped to xtablo's existing design tokens. Then rewrite ChatMessages.tsx in the main app to consume the new package.

Tech Stack: React 19, Tailwind CSS 4, TypeScript 5, Turborepo/pnpm workspaces


File Map

New files (packages/chat-ui/):

  • packages/chat-ui/package.json — Package manifest
  • packages/chat-ui/tsconfig.json — TypeScript config
  • packages/chat-ui/src/index.ts — Barrel export (copied from chatcn, unchanged)
  • packages/chat-ui/src/types.ts — Type definitions (copied from chatcn, ChatTheme removed)
  • packages/chat-ui/src/hooks.ts — Hooks (copied from chatcn, import remapped)
  • packages/chat-ui/src/security.ts — Security utils (copied from chatcn, unchanged)
  • packages/chat-ui/src/components/chat.tsx — Core components (copied, imports + theme adapted)
  • packages/chat-ui/src/components/features.tsx — Feature components (copied, imports + theme adapted)
  • packages/chat-ui/src/components/layouts.tsx — Layout components (copied, imports + theme adapted)
  • packages/chat-ui/src/chat-ui.css — Minimal CSS for animations and utility classes

Modified files:

  • apps/main/package.json — Remove chatscope deps, add @xtablo/chat-ui
  • apps/main/src/components/ChatMessages.tsx — Rewrite to use @xtablo/chat-ui

Complete Token Mapping Reference

Every var(--chat-*) occurrence in chatcn is replaced with a Tailwind class. This table is the single source of truth for all theme adaptation across Tasks 4-6.

chatcn CSS variable Tailwind class (as bg-) Tailwind class (as text-) Tailwind class (as border-) Notes
--chat-bg-app bg-background
--chat-bg-main bg-background
--chat-bg-sidebar bg-card Using card for secondary surfaces
--chat-bg-header bg-card With backdrop-blur kept
--chat-bg-composer bg-card With backdrop-blur kept
--chat-bg-code bg-muted Code block backgrounds
--chat-bg-hover bg-accent Hover states
--chat-bg-content-card bg-card Embedded content cards
--chat-bubble-outgoing bg-primary
--chat-bubble-outgoing-text text-primary-foreground
--chat-bubble-incoming bg-muted
--chat-bubble-incoming-text text-foreground
--chat-accent bg-primary text-primary chatcn accent = xtablo primary
--chat-accent-soft bg-accent xtablo accent is the soft/subtle bg
--chat-text-primary text-foreground
--chat-text-secondary text-muted-foreground
--chat-text-tertiary text-muted-foreground/60 Slightly more faded
--chat-border border-border
--chat-border-strong border-border Same token, stronger is unnecessary
--chat-red text-destructive
--chat-orange text-orange-500 No xtablo token; use Tailwind orange
--chat-green text-green-500 Presence indicator; use Tailwind green
--chat-font-mono font-mono Tailwind built-in
--chat-shadow-lg shadow-lg Tailwind built-in
--chat-shadow-md shadow-md Tailwind built-in
--chat-shadow-toolbar shadow-md

Task 1: Create package scaffold

Files:

  • Create: packages/chat-ui/package.json

  • Create: packages/chat-ui/tsconfig.json

  • Step 1: Create package.json

{
  "name": "@xtablo/chat-ui",
  "version": "0.0.1",
  "private": true,
  "type": "module",
  "exports": {
    ".": "./src/index.ts",
    "./components/*": "./src/components/*.tsx",
    "./hooks": "./src/hooks.ts",
    "./security": "./src/security.ts",
    "./types": "./src/types.ts"
  },
  "scripts": {
    "typecheck": "tsc --noEmit",
    "lint": "biome check .",
    "lint:fix": "biome check --write .",
    "format": "biome format --write ."
  },
  "dependencies": {
    "@xtablo/shared": "workspace:*",
    "@xtablo/ui": "workspace:*"
  },
  "peerDependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "lucide-react": "*",
    "date-fns": "*"
  },
  "devDependencies": {
    "@biomejs/biome": "2.2.5",
    "@types/react": "19.0.10",
    "@types/react-dom": "19.0.4",
    "typescript": "^5.7.0"
  }
}
  • Step 2: Create tsconfig.json

Copy verbatim from packages/ui/tsconfig.json:

{
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true,
    "esModuleInterop": true,
    "incremental": false,
    "isolatedModules": true,
    "lib": ["es2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleDetection": "force",
    "moduleResolution": "bundler",
    "noUncheckedIndexedAccess": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "strict": true,
    "target": "ES2022",
    "jsx": "react-jsx"
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}
  • Step 3: Run pnpm install to register the new workspace package

Run: cd /Users/arthur.belleville/Documents/perso/projects/xtablo-source && pnpm install Expected: lockfile updated, @xtablo/chat-ui recognized as workspace package

  • Step 4: Commit
git add packages/chat-ui/package.json packages/chat-ui/tsconfig.json pnpm-lock.yaml
git commit -m "feat(chat-ui): scaffold @xtablo/chat-ui package"

Task 2: Copy non-component source files (types, hooks, security)

Files:

  • Create: packages/chat-ui/src/types.ts

  • Create: packages/chat-ui/src/hooks.ts

  • Create: packages/chat-ui/src/security.ts

  • Source: chatcn/src/components/ui/chat/types.ts, hooks.ts, security.ts

  • Step 1: Copy types.ts from chatcn and remove ChatTheme

Copy /Users/arthur.belleville/Documents/perso/projects/chatcn/src/components/ui/chat/types.ts to packages/chat-ui/src/types.ts.

Remove the last line:

export type ChatTheme = "lunar" | "aurora" | "ember" | "midnight"

No other changes needed — this file has no imports.

  • Step 2: Copy security.ts from chatcn

Copy /Users/arthur.belleville/Documents/perso/projects/chatcn/src/components/ui/chat/security.ts to packages/chat-ui/src/security.ts.

No changes needed — this file has no imports from chatcn internals.

  • Step 3: Copy hooks.ts from chatcn and fix the "use client" directive

Copy /Users/arthur.belleville/Documents/perso/projects/chatcn/src/components/ui/chat/hooks.ts to packages/chat-ui/src/hooks.ts.

Remove the "use client" directive at the top (not needed in a Vite app — it's a Next.js RSC directive).

The import from "./types" stays — it's a relative import within the same package.

  • Step 4: Verify types compile

Run: cd /Users/arthur.belleville/Documents/perso/projects/xtablo-source && pnpm --filter @xtablo/chat-ui typecheck Expected: no type errors

  • Step 5: Commit
git add packages/chat-ui/src/types.ts packages/chat-ui/src/hooks.ts packages/chat-ui/src/security.ts
git commit -m "feat(chat-ui): add types, hooks, and security utilities from chatcn"

Task 3: Create chat-ui.css with animations and utility classes

Files:

  • Create: packages/chat-ui/src/chat-ui.css

chatcn uses several CSS classes for animations and utility styles that aren't Tailwind classes. These need a small CSS file.

  • Step 1: Create chat-ui.css
/* @xtablo/chat-ui — Animations and utility classes */

/* ─── Message entry ─────────────────────────────────────────────── */
@keyframes chat-message-enter {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ─── Toolbar entrance ──────────────────────────────────────────── */
@keyframes chat-toolbar-enter {
  from { opacity: 0; transform: scale(0.95) translateY(4px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}

/* ─── Reaction pop ──────────────────────────────────────────────── */
@keyframes chat-reaction-pop {
  0%   { transform: scale(0); opacity: 0; }
  70%  { transform: scale(1.1); }
  100% { transform: scale(1); opacity: 1; }
}

/* ─── Typing indicator dots ─────────────────────────────────────── */
@keyframes chat-typing-pulse {
  0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-4px); }
}

/* ─── Cursor blink (streaming) ──────────────────────────────────── */
@keyframes chat-cursor-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

/* ─── Read receipt status color transition ───────────────────────── */
@keyframes chat-status-read-in {
  from { color: var(--color-muted-foreground); }
  to   { color: var(--color-primary); }
}

/* ─── Utility classes ───────────────────────────────────────────── */
@layer base {
  .chat-message {
    animation: chat-message-enter 250ms cubic-bezier(0.25, 0.1, 0.25, 1.0);
  }

  .chat-typing-dot {
    animation: chat-typing-pulse 1.4s ease-in-out infinite;
  }

  .chat-toolbar-enter {
    transform-origin: bottom center;
  }

  .group\/message:hover .chat-toolbar-enter {
    animation: chat-toolbar-enter 150ms ease-out;
  }

  .chat-reaction-pop {
    animation: chat-reaction-pop 200ms cubic-bezier(0.25, 0.1, 0.25, 1.0);
  }

  .chat-status-read {
    animation: chat-status-read-in 400ms ease-out;
  }

  .chat-streaming-cursor {
    animation: chat-cursor-blink 1s step-end infinite;
  }

  .chat-content-card {
    background: var(--color-card);
    border: 1px solid var(--color-border);
    border-radius: 12px;
    overflow: hidden;
  }

  .chat-drop-overlay {
    position: absolute;
    inset: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in oklch, var(--color-background) 80%, transparent);
    border: 2px dashed var(--color-primary);
    border-radius: 12px;
    backdrop-filter: blur(4px);
  }
}

/* ─── Reduced motion ────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .chat-message,
  .chat-typing-dot,
  .chat-toolbar-enter,
  .chat-reaction-pop,
  .chat-status-read {
    animation: none;
  }
}

Note: This CSS uses var(--color-*) (Tailwind's theme inline variables, e.g., --color-primary maps to xtablo's --primary). This is how you reference design tokens from CSS in Tailwind v4.

  • Step 2: Commit
git add packages/chat-ui/src/chat-ui.css
git commit -m "feat(chat-ui): add animation and utility CSS"

Task 4: Copy and adapt chat.tsx (core components)

Files:

  • Create: packages/chat-ui/src/components/chat.tsx
  • Source: chatcn/src/components/ui/chat/chat.tsx (1415 lines)

This is the largest file. Apply three systematic transformations:

  1. Remove "use client" directive (line 0)
  2. Remap imports (lines 2-42)
  3. Replace all var(--chat-*) with Tailwind classes (throughout)
  4. Remove theme prop from ChatProvider and data-chat-theme attribute
  • Step 1: Copy the file

Copy /Users/arthur.belleville/Documents/perso/projects/chatcn/src/components/ui/chat/chat.tsx to packages/chat-ui/src/components/chat.tsx.

  • Step 2: Remove "use client" directive

Delete the first line "use client".

  • Step 3: Remap imports

Replace:

import { cn } from "@/lib/utils"

With:

import { cn } from "@xtablo/shared/lib/cn"

The import type { ..., ChatTheme } from "./types" — remove ChatTheme from this import.

All other imports (React, lucide-react, createPortal, local ./types, ./hooks) stay as-is since they're either external packages or relative imports within the package.

  • Step 4: Remove theme from ChatProvider

In ChatProviderProps interface (~line 57-71): remove theme?: ChatTheme prop.

In ChatProvider function (~line 73-110):

  • Remove theme = "lunar" from destructured params

  • Remove data-chat-theme={theme} from the wrapping div — change to just <div style={style} className={className}>

  • Step 5: Replace all var(--chat-*) occurrences with Tailwind classes

This is the bulk of the work. Apply the token mapping table from above systematically throughout the file. The pattern is to replace inline var(--chat-*) within className strings.

Examples of transformations:

// BEFORE
bg-[var(--chat-bg-sidebar)]
// AFTER
bg-card

// BEFORE
text-[var(--chat-text-primary)]
// AFTER
text-foreground

// BEFORE
text-[var(--chat-text-secondary)]
// AFTER
text-muted-foreground

// BEFORE
text-[var(--chat-text-tertiary)]
// AFTER
text-muted-foreground/60

// BEFORE
border-[var(--chat-border)]
// AFTER
border-border

// BEFORE
border-[var(--chat-border-strong)]
// AFTER
border-border

// BEFORE
bg-[var(--chat-accent-soft)]
// AFTER
bg-accent

// BEFORE
text-[var(--chat-accent)]
// AFTER
text-primary

// BEFORE
bg-[var(--chat-accent)]
// AFTER
bg-primary

// BEFORE
bg-[var(--chat-bubble-outgoing)]
// AFTER
bg-primary

// BEFORE
text-[var(--chat-bubble-outgoing-text)]
// AFTER
text-primary-foreground

// BEFORE
bg-[var(--chat-bubble-incoming)]
// AFTER
bg-muted

// BEFORE
text-[var(--chat-bubble-incoming-text)]
// AFTER
text-foreground

// BEFORE
bg-[var(--chat-bg-main)]
// AFTER
bg-background

// BEFORE
bg-[var(--chat-bg-composer)]
// AFTER
bg-card

// BEFORE
shadow-[var(--chat-shadow-toolbar)]
// AFTER
shadow-md

// BEFORE
shadow-[var(--chat-shadow-lg)]
// AFTER
shadow-lg

// BEFORE
shadow-[var(--chat-shadow-md)]
// AFTER
shadow-md

// BEFORE
text-[var(--chat-red)]
// AFTER
text-destructive

// BEFORE
bg-[var(--chat-bg-code)]
// AFTER
bg-muted

// BEFORE
style={{ fontFamily: "var(--chat-font-mono)" }}
// AFTER (use className instead)
className="font-mono ..."

For inline style attributes that reference var(--chat-*):

  • style={{ background: "var(--chat-accent)" }} → replace with className using bg-primary
  • In ChatVoiceMessage, the waveform bars use inline style with var(--chat-accent) — replace with var(--color-primary) (Tailwind v4 resolved token)
  • In ChatVoiceMessage, style={{ color: "white" }} stays (it's already a concrete value)

Special cases in ChatMessageReply:

  • border-[var(--chat-bubble-outgoing-text)]/30border-primary-foreground/30
  • bg-[var(--chat-bubble-outgoing-text)]/10bg-primary-foreground/10

Special cases in ChatMessageReactions:

  • border-[var(--chat-accent)]/30border-primary/30

  • Step 6: Verify types compile

Run: cd /Users/arthur.belleville/Documents/perso/projects/xtablo-source && pnpm --filter @xtablo/chat-ui typecheck Expected: no type errors (or only errors from missing features.tsx/layouts.tsx imports, which is fine for now)

  • Step 7: Commit
git add packages/chat-ui/src/components/chat.tsx
git commit -m "feat(chat-ui): add core chat components with xtablo theming"

Task 5: Copy and adapt features.tsx

Files:

  • Create: packages/chat-ui/src/components/features.tsx

  • Source: chatcn/src/components/ui/chat/features.tsx (510 lines)

  • Step 1: Copy the file

Copy /Users/arthur.belleville/Documents/perso/projects/chatcn/src/components/ui/chat/features.tsx to packages/chat-ui/src/components/features.tsx.

  • Step 2: Remove "use client" and remap imports

Remove "use client" directive.

Replace:

import { cn } from "@/lib/utils"

With:

import { cn } from "@xtablo/shared/lib/cn"

Other imports (lucide-react, ./types, ./hooks) stay as-is.

  • Step 3: Replace all var(--chat-*) with Tailwind classes

Apply the same token mapping as Task 4. This file uses these tokens:

  • var(--chat-border-strong) → remove bracket, use border-border

  • var(--chat-bg-sidebar)bg-card

  • var(--chat-shadow-lg)shadow-lg

  • var(--chat-shadow-toolbar)shadow-md

  • var(--chat-border)border-border

  • var(--chat-text-primary)text-foreground

  • var(--chat-text-secondary)text-muted-foreground

  • var(--chat-text-tertiary)text-muted-foreground/60

  • var(--chat-bg-main)bg-background

  • var(--chat-accent-soft)bg-accent

  • var(--chat-bubble-incoming)bg-muted

  • var(--chat-accent)text-primary or bg-primary depending on context

  • var(--chat-red)text-destructive

  • var(--chat-orange)text-orange-500

  • var(--chat-bg-composer)bg-card

  • Step 4: Commit

git add packages/chat-ui/src/components/features.tsx
git commit -m "feat(chat-ui): add feature components with xtablo theming"

Task 6: Copy and adapt layouts.tsx

Files:

  • Create: packages/chat-ui/src/components/layouts.tsx

  • Source: chatcn/src/components/ui/chat/layouts.tsx (822 lines)

  • Step 1: Copy the file

Copy /Users/arthur.belleville/Documents/perso/projects/chatcn/src/components/ui/chat/layouts.tsx to packages/chat-ui/src/components/layouts.tsx.

  • Step 2: Remove "use client" and remap imports

Remove "use client" directive.

Replace:

import { cn } from "@/lib/utils"

With:

import { cn } from "@xtablo/shared/lib/cn"

Remove ChatTheme from the type imports:

import type { ChatMessageData, ChatUser, TypingUser } from "./types"

The imports from "./chat" stay as relative:

import { ChatProvider, ChatMessages, ChatComposer } from "./chat"
  • Step 3: Remove theme prop from all layout components

Every layout component (FullMessenger, ChatWidget, InlineChat, ChatBoard, LiveChat) has a theme?: ChatTheme prop passed to <ChatProvider>. Remove it from:

  1. Each component's Props interface — remove theme?: ChatTheme
  2. Each component's destructured params — remove theme = "lunar"
  3. Each <ChatProvider> usage — remove theme={theme}
  • Step 4: Replace all var(--chat-*) with Tailwind classes

Same mapping as previous tasks. This file uses the same set of tokens plus:

  • var(--chat-bg-app)bg-background

  • var(--chat-bg-header)bg-card

  • var(--chat-green)text-green-500 / bg-green-500

  • Step 5: Commit

git add packages/chat-ui/src/components/layouts.tsx
git commit -m "feat(chat-ui): add layout components with xtablo theming"

Task 7: Create barrel export (index.ts)

Files:

  • Create: packages/chat-ui/src/index.ts

  • Source: chatcn/src/components/ui/chat/index.ts

  • Step 1: Copy index.ts from chatcn

Copy /Users/arthur.belleville/Documents/perso/projects/chatcn/src/components/ui/chat/index.ts to packages/chat-ui/src/index.ts.

  • Step 2: Update import paths

The chatcn index.ts uses "./chat", "./features", "./layouts", "./security", "./types", "./hooks". Update the component imports to point into the components/ subdirectory:

Replace:

} from "./chat"

With:

} from "./components/chat"

Replace:

} from "./features"

With:

} from "./components/features"

Replace:

} from "./layouts"

With:

} from "./components/layouts"

The "./security", "./types", and "./hooks" imports stay as-is (they're at the src root).

Also remove ChatTheme from the types export section:

// Remove this line from the type exports:
// ChatTheme,
  • Step 3: Add CSS export

Add at the top of the file:

import "./chat-ui.css"

This ensures the CSS is included when the package is consumed.

  • Step 4: Verify full package compiles

Run: cd /Users/arthur.belleville/Documents/perso/projects/xtablo-source && pnpm --filter @xtablo/chat-ui typecheck Expected: PASS with no errors

  • Step 5: Commit
git add packages/chat-ui/src/index.ts
git commit -m "feat(chat-ui): add barrel export and wire up CSS"

Task 8: Update main app dependencies

Files:

  • Modify: apps/main/package.json

  • Step 1: Add @xtablo/chat-ui dependency

In apps/main/package.json, add to the dependencies section:

"@xtablo/chat-ui": "workspace:*",
  • Step 2: Remove chatscope dependencies

Remove these two lines from dependencies:

"@chatscope/chat-ui-kit-react": "^2.1.1",
"@chatscope/chat-ui-kit-styles": "^1.4.0",
  • Step 3: Run pnpm install

Run: cd /Users/arthur.belleville/Documents/perso/projects/xtablo-source && pnpm install Expected: lockfile updated, chatscope removed, @xtablo/chat-ui linked

  • Step 4: Commit
git add apps/main/package.json pnpm-lock.yaml
git commit -m "feat(main): switch from chatscope to @xtablo/chat-ui"

Task 9: Rewrite ChatMessages.tsx to use @xtablo/chat-ui

Files:

  • Modify: apps/main/src/components/ChatMessages.tsx

This is the core integration point. The current file is 184 lines using chatscope components. Replace it entirely with a new implementation using @xtablo/chat-ui.

  • Step 1: Rewrite ChatMessages.tsx

Replace the entire contents of apps/main/src/components/ChatMessages.tsx with:

import { useMemo } from "react";
import {
  ChatProvider,
  ChatMessages as ChatMessageList,
  ChatComposer,
  ChatTypingIndicator,
} from "@xtablo/chat-ui";
import type { ChatMessageData, ChatUser, TypingUser } from "@xtablo/chat-ui";

interface ChatMessage {
  id: string;
  userId: string;
  text: string;
  createdAt: string;
  clientId: string;
  optimistic?: boolean;
}

interface Member {
  id: string;
  name: string;
  avatar_url: string | null;
}

interface ChatMessagesProps {
  messages: ChatMessage[];
  currentUserId: string;
  members: Member[];
  typingUsers: string[];
  hasMoreMessages: boolean;
  onLoadMore?: () => void;
  onSend: (text: string) => void;
  onTyping: () => void;
  placeholder?: string;
}

export function ChatMessages({
  messages,
  currentUserId,
  members,
  typingUsers,
  hasMoreMessages,
  onLoadMore,
  onSend,
  onTyping,
  placeholder = "Envoyer un message...",
}: ChatMessagesProps) {
  const membersById = useMemo(() => {
    const map = new Map<string, Member>();
    for (const m of members) {
      map.set(m.id, m);
    }
    return map;
  }, [members]);

  const currentUser = useMemo<ChatUser>(
    () => ({
      id: currentUserId,
      name: membersById.get(currentUserId)?.name ?? "Moi",
      avatar: membersById.get(currentUserId)?.avatar_url ?? undefined,
    }),
    [currentUserId, membersById],
  );

  const chatMessages = useMemo<ChatMessageData[]>(
    () =>
      messages.map((msg) => {
        const member = membersById.get(msg.userId);
        return {
          id: msg.id,
          senderId: msg.userId,
          senderName: member?.name ?? "Utilisateur",
          senderAvatar: member?.avatar_url ?? undefined,
          text: msg.text,
          timestamp: new Date(msg.createdAt),
          status: msg.optimistic ? "sending" : undefined,
        };
      }),
    [messages, membersById],
  );

  const chatTypingUsers = useMemo<TypingUser[]>(
    () =>
      typingUsers.map((userId) => ({
        id: userId,
        name: membersById.get(userId)?.name ?? "Utilisateur",
        avatar: membersById.get(userId)?.avatar_url ?? undefined,
      })),
    [typingUsers, membersById],
  );

  return (
    <ChatProvider
      currentUser={currentUser}
      className="flex h-full flex-col"
    >
      <ChatMessageList
        messages={chatMessages}
        typingUsers={chatTypingUsers}
      />
      <ChatComposer
        onSend={onSend}
        onTyping={(_isTyping) => {
          if (_isTyping) onTyping();
        }}
        placeholder={placeholder}
      />
    </ChatProvider>
  );
}

Key decisions:

  • Props interface stays identicalChatPage and TabloDiscussionSection don't need changes.

  • Data transformation happens in useMemo — maps xtablo's flat message/member model to chatcn's richer types.

  • Typing callback bridge — chatcn's onTyping fires (isTyping: boolean) while xtablo's onTyping / sendTyping is fire-and-forget. We only call onTyping() when typing starts.

  • Localization — French strings ("Utilisateur", "Moi", "Envoyer un message...") are kept at the app level as before. The chatcn formatDateLabel returns English labels ("Today", "Yesterday") — this is acceptable for now and can be overridden later with i18next.

  • Step 2: Import the CSS in the main app

In apps/main/src/main.css, add the chat-ui CSS import at the top (after tailwindcss):

@import "tailwindcss";
@import "tw-animate-css";
@import "@xtablo/chat-ui/src/chat-ui.css";

Alternatively, if the import "./chat-ui.css" in index.ts works via Vite's CSS handling (it should, since Vite processes CSS imports in JS), this explicit import may not be needed. Test by checking if animations work in the browser. If they do, skip this step.

  • Step 3: Verify types compile

Run: cd /Users/arthur.belleville/Documents/perso/projects/xtablo-source && pnpm --filter @xtablo/main typecheck Expected: PASS with no errors

  • Step 4: Commit
git add apps/main/src/components/ChatMessages.tsx
git commit -m "feat(main): rewrite ChatMessages to use @xtablo/chat-ui"

Task 10: Visual testing and cleanup

Files:

  • Possibly modify: apps/main/src/main.css (CSS import if needed)

  • Possibly modify: packages/chat-ui/src/components/chat.tsx (style fixes)

  • Step 1: Start the dev server

Run: cd /Users/arthur.belleville/Documents/perso/projects/xtablo-source && pnpm dev:main Expected: Main app starts on port 5173

  • Step 2: Test the chat page

Open http://localhost:5173 in a browser. Navigate to a chat channel. Verify:

  1. Messages render correctly (incoming on left, outgoing on right)
  2. Avatars show for incoming messages (initials fallback if no avatar URL)
  3. Message grouping works (consecutive messages from same sender are grouped)
  4. Date separators appear between days
  5. Typing indicator shows when someone is typing
  6. Composer works — type a message and send with Enter
  7. Auto-scroll to bottom on new messages
  8. Scroll-to-bottom button appears when scrolled up
  9. Optimistic messages show with sending status (clock icon)
  10. Dark mode looks correct (toggle dark mode if available)
  • Step 3: Test the tablo discussion section

Navigate to a tablo detail page that has a discussion section. Verify the chat works there too — same component, same behavior.

  • Step 4: Fix any visual issues

If any theme tokens don't look right (e.g., contrast issues, colors too similar), adjust the mapping in the affected component file. Common issues:

  • If text-muted-foreground/60 is too faint, try text-muted-foreground/70

  • If outgoing bubble color (bg-primary) doesn't feel "chat-like", consider adding a chat-specific CSS variable in main.css

  • Step 5: Remove chatscope CSS import if still present anywhere

Search for any remaining chatscope references:

Run: grep -r "chatscope" apps/main/src/ Expected: no results

  • Step 6: Final typecheck and lint

Run: cd /Users/arthur.belleville/Documents/perso/projects/xtablo-source && pnpm typecheck && pnpm lint Expected: PASS

  • Step 7: Commit any fixes
git add -A
git commit -m "fix(chat-ui): visual polish and cleanup after integration"

Post-Integration Notes

After all tasks are complete:

  • chatscope is fully removed — no dependencies, no CSS import, no component usage
  • @xtablo/chat-ui is a self-contained workspace package with all chatcn components
  • ChatMessages.tsx keeps its original props interface — no changes needed in consuming components
  • Future features (reactions, threads, search, pinned messages) are available in the package and can be wired up incrementally
  • Localization — date labels are in English. To switch to French, create a custom formatDateLabel using i18next and pass translated labels, or override the function in the package