# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

This is **Mori Widget** - an embeddable AI-powered chat widget library for e-commerce websites. It's distributed as an NPM package and served via CDN (jsDelivr). The widget is designed for Persian/Farsi (RTL) interfaces and includes product-aware chat capabilities with WooCommerce integration.

**Tech Stack:** React 19 + TypeScript + Vite + Chakra UI 3 + Emotion + Shadow DOM

## Development Commands

### Core Workflows
```bash
pnpm dev              # Dev server on port 3000 - NO Shadow DOM (easier debugging)
pnpm build            # Production build - WITH Shadow DOM + CSS injection
pnpm type-check       # TypeScript validation without build
pnpm lint             # ESLint check
pnpm lint:fix         # Auto-fix linting + remove unused imports
pnpm format           # Prettier formatting
```

### Build Configuration Differences

**Development (`vite.config.dev.ts`):**
- Entry: `./index.html` (standard SPA)
- Direct rendering without Shadow DOM
- Hot module replacement enabled
- Full console logs and debugging

**Production (`vite.config.ts`):**
- Entry: `/src/unified-widget.ts` (widget initialization)
- Shadow DOM isolation for style encapsulation
- CSS injected into JS bundle (no separate .css file)
- Font URLs rewritten for CDN (`https://cdn.jsdelivr.net/npm/mori-widget@{version}/dist/`)
- Console logs stripped via Terser
- Sentry integration with release tracking
- Tree-shaking optimizations

## High-Level Architecture

### Dual Widget System

The codebase supports **two widget types** that can coexist on the same page:

1. **Floating Widget** (`App.tsx`): Global FAB (Floating Action Button) + modal chat overlay
2. **Embedded Widget** (`components/embedded-widget/`): Inline PDP (Product Detail Page) widget

Both widgets share the same `ChatProvider` state but maintain separate:
- Session storage keys (`moriFloatingWidgetState` vs `moriEmbeddedWidgetState`)
- UI configurations (colors, positioning)
- Visibility states

### Initialization Flow

```
Script Load (unified-widget.ts)
    ↓
Prefetch Bot Data (fetchBotData)
    ↓
Dynamic Import main.tsx
    ↓
initializeDoriWidget()
    ├── Detect installation (WordPress vs direct script)
    ├── Find mount points (#dori-chatbot-root, .mori-embedded-widget)
    ├── Create React roots
    └── Wrap in Provider (Shadow DOM) + ChatProvider
    ↓
Render App.tsx (floating) and/or EmbeddedWidget.tsx
```

### Shadow DOM Isolation Strategy

**Why:** Prevents CSS conflicts with host website styles.

**Implementation:**
- `react-shadow/emotion` wraps entire widget tree (see `providers/provider.tsx`)
- Emotion cache scoped to shadow root
- Chakra UI system configured with custom emotion cache
- **Critical:** All styles must be injected into shadow DOM, not `<head>`

**Development vs Production:**
- Dev: No Shadow DOM (set `USE_SHADOW_DOM=false` in `main.tsx`)
- Production: Always uses Shadow DOM

### State Management Architecture

**ChatProvider** (`providers/chat/chat-context.tsx`) is the single source of truth for:
- Bot configuration and branding
- Message history (user + assistant)
- Thread/conversation management
- Streaming response state
- Product context (current product on page)
- Session persistence via `sessionStorage`

**Key State Flow:**
```
User sends message
    ↓
createSendMessage (chat/actions/message-actions.ts)
    ↓
API Stream (utils/api.ts → runAssistant + streamV2)
    ↓
processStreamingResponse (chat/streaming/stream-handlers.ts)
    ↓
State update → SessionStorage sync → Re-render
```

### Streaming Response Architecture

The widget uses a **V2 streaming response** pattern for real-time chat:

1. **API Layer** (`utils/api.ts`):
   - Step 1: POST to `/v2/run-assistant` to start the stream (returns `threadId` + `runId`)
   - Step 2: GET `/v2/stream` with `threadId` and `runId` to receive chunks
   - Response: `ReadableStream` of JSON chunks

2. **Stream Processing** (`providers/chat/streaming/stream-handlers.ts`):
   - Reader loop processes chunks via `chunk-handler.ts`
   - Accumulates content in real-time
   - Updates message state on each chunk
   - Handles completion signals

3. **Cancellation Support**:
   - AbortController passed to fetch
   - User can stop generation mid-stream

### Session Persistence Mechanism

**Purpose:** Preserve chat state across page navigations and browser refreshes.

**Storage Keys:**
- `moriFloatingWidgetState`: Floating widget state
- `moriEmbeddedWidgetState`: Embedded widget state
- `mori_wc_session_id`: WooCommerce session ID

**Persisted Data:**
```typescript
{
  messages: Message[]           // Full chat history
  threadId: string | null       // Current conversation thread
  isOpen: boolean              // Widget visibility
  currentScreen: ScreenType    // welcome | suggestions | chat
}
```

**Cross-Tab Sync** (`utils/session-storage.ts`):
- Custom events: `mori:state-updated`, `mori:open-widget`, `mori:close-widget`
- Allows multiple tabs to share widget state

### Screen State Machine

The widget follows a **linear progression** through screens:

```
WelcomeScreen (animated greeting)
    ↓ (auto-transition after 3s OR user click)
SuggestionScreen (quick actions)
    ↓ (user selects suggestion OR types message)
ChatScreen (full chat interface)
```

**Implementation:** `hooks/useScreenState.ts`
- State: `welcome | suggestions | chat`
- Transitions managed by `ChatProvider`
- Animated fade transitions via Framer Motion

## Key Technical Patterns

### Error Handling Architecture

**Layered Approach:**

1. **Sentry Integration** (`utils/sentry.ts`):
   - Global error tracking
   - Breadcrumbs for user actions
   - Release/environment tagging
   - User context (sharing ID, session)
   - Sentry is fully disabled in development: `client` is `null` and `scope` is a no-op object with empty functions. In production, `SAMPLE_RATE` (0.01 = 1%) is applied to both `sampleRate` and `tracesSampleRate`. Any future profiling sampling knobs should reuse `SAMPLE_RATE` to ensure production remains capped at 1%.

2. **HOC Wrappers**:
   - `withErrorHandling`: Wraps sync functions, logs to Sentry
   - `withAsyncErrorHandling`: Wraps async functions, handles promise rejections

3. **React Error Boundaries** (`components/ErrorBoundary.tsx`):
   - Catches rendering errors
   - Displays fallback UI
   - Logs to Sentry with component stack

4. **API Error Handling** (`utils/api.ts`):
   - Consistent error response parsing
   - Network failure detection
   - Graceful degradation (show error message in chat)

### Product Context Awareness

**Problem:** Chat should know which product the user is viewing.

**Solution:** `hooks/useCurrentProduct.ts`

1. **WooCommerce Detection**:
   - Checks for `woocommerce` or `wc-blocks` in `<body>` classes
   - Looks for `.product` DOM elements with `data-product-id`

2. **URL Parsing**:
   - Extracts product ID from URL patterns
   - Supports custom product URL structures

3. **Context Injection**:
   - Product ID + URL sent with first message
   - Allows AI to provide product-specific responses

### API Layer Abstraction

All API calls centralized in `utils/api.ts`:

```typescript
// Core endpoints (base: https://standardapi.dori.tech)
fetchBotDataBySharingID(sharingId)  // GET bot config from /bot-sharing-data/
// V2 Streaming endpoints
runAssistant(payload, abort)        // POST /v2/run-assistant (start stream)
streamV2(threadId, runId, abort)    // GET /v2/stream (receive chunks)
// Tracking endpoints
trackProductView(...)               // POST /roi/track-view
trackSession(...)                   // POST /roi/track-session
```

**Consistent Patterns:**
- Request deduplication for concurrent identical requests
- Sentry breadcrumbs for all requests
- AbortController support for cancellation
- Standardized error handling with retry logic
- Authorization headers (X-Sharing-Id)

## Integration Points

### Script Tag Installation

**Standard Installation:**
```html
<script src="https://cdn.jsdelivr.net/npm/mori-widget@latest/dist/widget.js"
        data-sharing-id="YOUR_ID"></script>
```

**Widget Detection:**
- `unified-widget.ts` reads `data-sharing-id` from script tag
- Prefetches bot configuration before rendering

### Global API

The widget exposes `window.moriWidget`:

```typescript
window.moriWidget = {
  open: () => void      // Open floating widget
  close: () => void     // Close floating widget
  toggle: () => void    // Toggle floating widget
}
```

**Usage:** Allows host website to control widget programmatically.

### Custom Events

**Dispatch Events:**
```javascript
window.dispatchEvent(new CustomEvent('mori:open-widget'))
window.dispatchEvent(new CustomEvent('mori:close-widget'))
window.dispatchEvent(new CustomEvent('mori:toggle-widget'))
```

**Listen Events:**
- `mori:state-updated`: Cross-tab state synchronization

### WooCommerce Integration

**Session Tracking:**
- Reads `wc_session` cookie or generates UUID
- Stored in `sessionStorage` as `mori_wc_session_id`
- Sent with all API requests for user identification

**Product Detection:**
- Automatic product context on PDP pages
- See "Product Context Awareness" section

## Critical Code Locations

### Entry Points
- `src/unified-widget.ts` - Initial script execution and prefetch
- `src/main.tsx:initializeDoriWidget()` - React initialization

### State Management
- `src/providers/chat/chat-context.tsx` - ChatProvider (main state + context)
- `src/providers/chat/actions/` - State mutations organized by domain:
  - `bot-actions.ts` - Bot initialization
  - `message-actions.ts` - Message sending
  - `conversation-actions.ts` - Thread/conversation management
- `src/utils/session-storage.ts` - Persistence layer

### UI Components
- `src/App.tsx` - Floating widget container
- `src/components/embedded-widget/EmbeddedWidget.tsx` - PDP widget
- `src/components/widget/Widget.tsx` - Screen orchestrator

### API & Utilities
- `src/utils/api.ts` - All HTTP requests (includes `API_URLS` constant with all endpoints)
- `src/providers/chat/streaming/` - Stream processing:
  - `stream-handlers.ts` - Main streaming logic
  - `chunk-handler.ts` - Individual chunk processing
- `src/utils/sentry.ts` - Error tracking setup
- `src/utils/posthog.ts` - Analytics tracking

## Path Aliases

TypeScript configured with path aliases (`tsconfig.json`):
```typescript
import { api } from '@/utils/api'     // Same as '~/utils/api'
import { ChatProvider } from '~/providers/chat'
```

Both `@/*` and `~/*` map to `./src/*`.

## Deployment Notes

- **CDN:** Widget served via jsDelivr NPM CDN
- **Versioning:** Font URLs rewritten during build to match package version
- **Bundles:** Single JS file with CSS injected (no separate .css)
- **Browser Support:** Modern browsers (ES2020+)
- **Asset Loading:** Fonts and images loaded from CDN (`dist/` folder)

## Common Pitfalls

1. **Shadow DOM Styling:** Never use global CSS or `<head>` injections. All styles must go through Emotion/Chakra UI.

2. **Dev vs Prod Rendering:** Features working in dev may break in prod due to Shadow DOM. Always test production builds.

3. **Session Storage Keys:** Floating and embedded widgets have separate storage. Don't mix them.

4. **Stream Cancellation:** Always pass AbortController to streaming requests to prevent memory leaks.

5. **Persian/RTL:** All UI text is RTL. Be mindful of direction when adding new components.

6. **Console Logs:** Stripped in production. Use Sentry for debugging prod issues.

7. **ESLint Flat Config:** Uses ESLint 9 flat config format (`eslint.config.js`). Unused imports are auto-removed by the `unused-imports` plugin.

8. **Unused Variables:** Prefix with underscore (`_var`) to satisfy linting rules for intentionally unused parameters.

## Files to Never Modify

**CRITICAL: NEVER read, edit, or modify the following:**

1. **`dist/` folder:** This is the production build output directory. All files here are auto-generated by `pnpm build`. Any manual changes will be overwritten on next build.
   - `dist/widget.js` - Minified production bundle
   - `dist/assets/*` - Build artifacts (CSS, JS chunks)
   - `dist/fonts/*` - Font assets

2. **Minified files:** Never attempt to read or edit any `.min.js`, `.min.css`, or other minified files anywhere in the codebase.

3. **Build artifacts:** Any file generated by the build process should be treated as read-only.

**What to do instead:**
- Always edit source files in `src/` directory
- Run `pnpm build` to regenerate production files
- Use `pnpm dev` for development with hot reload
