npm package: @sammy-labs/success
Description: Sammy Labs Toolkit: AI Customer Success
Author: Sammy Labs Team
Latest version: 0.2.2

# SAMMY Labs – AI Customer Success Toolkit

The AI engine powering post-sales success by generating personalized walkthroughs, documentation, and onboarding experiences that continuously evolve with your product—keeping users delighted, reducing churn, and driving long-term customer success.

SAMMY is an AI that navigates your app or website exactly like a real user, mapping every possible click to generate and personalize content for each user. It automatically updates all training materials, documentation, video tutorials, and user onboarding flows whenever your product changes—so your team never has to worry about outdated help content again.

---

## Why SAMMY?

### Future of Product-Led Growth (PLG)

- **Enhanced User Experience**  
  Users decide within minutes if your software meets their needs. SAMMY customizes every aspect of the help experience based on user context, ensuring a frictionless, relevant user journey that boosts adoption.

- **Auto-Generated Documentation & Training**  
  Every time your product updates, SAMMY regenerates context-aware documents, walkthroughs, and video tutorials.

- **AI-Powered Issue Detection**  
  SAMMY spots UI or feature bugs by continuously exploring your product, connecting issues to real user pain points.

- **Seamless Integrations**  
  Integrate with Slack, Intercom, HubSpot, Zendesk, Jira, and Notion.

---

## Key Features

- **AI Chatbot**  
  Conversational UI for users, with SSE streaming responses and optional live-human escalation.

- **Interactive Walkthroughs**  
  Provide step-by-step in-app guidance, with AI-driven element matching and fallback logic.

- **Auto-Updated Documentation**  
  Centralized knowledge base auto-syncs with your product's UI changes.

- **Context-Aware Help Center**  
  A floating panel that unifies Chat, Docs, and Interactive Flows into one experience.

- **Theme Support**  
  Built-in light/dark mode with system detection, easily customized.

- **JWT Expiry Handling**  
  Callback `onTokenExpired` to handle short-lived tokens and prompt for re-authentication.

- **Continuous UI Testing**  
  Automatic issue detection that correlates user conversations with potential UI bugs.

- **Onboarding Dashboard & Progress Tracking**  
  A dedicated "Walkthroughs" screen shows onboarding sections, real-time progress bars, and lets users resume or restart any flow.

- **Chat-Generated Walkthroughs**  
  Ask the bot "Show me how to…". Sammy will instantly generate an interactive walkthrough using the `@sammy-labs/walkthroughs` engine and launch it.

---

## Installation

```bash
npm install @sammy-labs/success
# or
yarn add @sammy-labs/success
# or
pnpm add @sammy-labs/success
```

Don't forget to import the CSS file in your application:

```tsx
import "@sammy-labs/success/dist/success.css";
```

## Optional Preflight Reset

If your project doesn't already reset element defaults (or you'd like Sammy's minimal scoped reset),
import:

```js
import "@sammy-labs/success/preflight.css";
```

---

## Quick Start

Below is a minimal example:

```tsx
import React from "react";
import { SammyProvider } from "@sammy-labs/success";
import "@sammy-labs/success/dist/success.css";

export function App() {
  // Typically, your token is a short-lived JWT from your backend
  const token = "YOUR_JWT_TOKEN";

  // Optionally, if you have a callback for token expiry:
  const handleTokenExpired = () => {
    // e.g. redirect to login or refresh token
    console.warn("Token expired! Re-authenticate user.");
  };

  return (
    <div>
      <SammyProvider
        // Main required fields
        baseUrl="https://api.yourapp.com"
        token={token}
        // These can be extracted from the token, or overridden:
        orgId="ORG_ID" // optional override
        user={{
          id: "USER_ID",
          email: "user@example.com",
          role: "user",
        }}
        // Additional UI & behavior props
        width="450px"
        height="700px"
        position="bottom-right"
        initialView="home"
        initialChatText="Hello! How can I help you today?"
        // Optional callback if token is invalid/expired
        onTokenExpired={handleTokenExpired}
        // Provide a custom logo, or fallback to your brand
        logo="https://yourapp.com/logo.png"
        botImage="https://yourapp.com/chat-bot.png"
        // Customize the button shape, theming, etc.
        buttonRoundedCorners="rounded-full"
        animated
        enableOnboarding={BOOLEAN}
        walkthroughOverrideDomainUrl="{OVERRIDE_BASE_URL}"
      />
      {/* The rest of your application */}
      <YourApp />
    </div>
  );
}
```

In this example:

1. **`SammyProvider`** wraps your main app or a portion of it.
2. **`token`** is a short-lived JWT; we do not store it in `localStorage` or `sessionStorage`, only in memory.
3. If `onTokenExpired` is provided, it is called whenever the token is invalid or has expired.
4. `orgId` and `user` can either be explicitly passed or derived automatically from the token.

Once the user clicks on the Sammy help button, they'll see a chat panel with integrated docs, AI suggestions, and more.

---

## Server-Side Token Generation

For secure authentication, you should generate JWT tokens on your server. Here's an example using a server action in Next.js:

```tsx
"use server";

interface AuthResponse {
  token: string;
  expires_at: string;
}

interface AuthRequest {
  api_key?: string;
  org_id: string;
  user_data: {
    user_id: string;
    first_name: string;
    last_name: string;
    email: string;
  };
}

export async function generateToken(
  baseUrl: string
): Promise<AuthResponse | null> {
  try {
    // Get API key from environment variables
    const apiKey = process.env.API_KEY;

    if (!apiKey) {
      console.error("API_KEY is not defined in environment variables");
      return null;
    }

    // Prepare the request body according to the API schema
    const authRequest: AuthRequest = {
      org_id: process.env.ORG_ID || "00000000-0000-0000-0000-000000000000",
      user_data: {
        user_id: process.env.USER_ID || "00000000-0000-0000-0000-000000000001",
        first_name: "Demo",
        last_name: "User",
        email: "demo@example.com",
      },
    };

    // Call the authentication endpoint to get a JWT token
    const response = await fetch(`${baseUrl}/authenticate`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${apiKey}`,
      },
      body: JSON.stringify(authRequest),
    });

    if (!response.ok) {
      // Log more details about the error
      const errorText = await response.text();
      console.error(
        `Failed to generate token: ${response.status} ${response.statusText}`
      );
      console.error(`Error details: ${errorText}`);
      return null;
    }

    const data = await response.json();
    return {
      token: data.token,
      expires_at:
        data.expires_at || new Date(Date.now() + 240 * 60 * 1000).toISOString(), // Fallback if expires_at is not provided
    };
  } catch (error) {
    console.error("Error generating token:", error);
    return null;
  }
}
```

You can then use this function in your client component:

```tsx
import { SammyProvider } from "@sammy-labs/success";
import "@sammy-labs/success/dist/success.css";
import { generateToken } from "./actions";
import { useEffect, useState } from "react";

export function App() {
  const [token, setToken] = useState<string | null>(null);

  useEffect(() => {
    async function fetchToken() {
      const authResponse = await generateToken("https://api.sammy-labs.com");
      if (authResponse) {
        setToken(authResponse.token);
      }
    }

    fetchToken();
  }, []);

  if (!token) return <div>Loading...</div>;

  return (
    <div>
      <SammyProvider
        baseUrl="https://api.sammy-labs.com"
        token={token}
        // Other props...
      />
      {/* The rest of your application */}
    </div>
  );
}
```

---

## Configuration Reference

Below are the main props you can pass to **`SammyProvider`**:

| **Prop**               | **Type**                                                       | **Default**        | **Required?** | **Description**                                                                                                                              |
| ---------------------- | -------------------------------------------------------------- | ------------------ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `baseUrl`              | `string`                                                       | —                  | **Yes**       | Base URL for your app's backend API, e.g., `https://api.yourapp.com`. This is where Sammy will fetch docs, chat, handle authentication, etc. |
| `token`                | `string`                                                       | —                  | **Yes**       | Short-lived JWT used for auth. Must be valid for calls to the back-end. Stored in memory only.                                               |
| `onTokenExpired`       | `() => void`                                                   | —                  | No            | Callback triggered if the token is invalid/expired (HTTP 401). Typically used to prompt a re-login or refresh the token.                     |
| `orgId`                | `string`                                                       | Derived from token | No            | If you prefer to override the organization ID extracted from the token, pass it here.                                                        |
| `user`                 | `UserCredentials`                                              | Derived from token | No            | If you prefer to override user fields from the token, pass them here (`id`, `email`, `role`, etc.).                                          |
| `theme`                | `"light" \| "dark"`                                            | System autodetect  | No            | Overrides the theme. If not provided, it follows the system preference, stored in context.                                                   |
| `logo`                 | `string`                                                       | —                  | No            | URL for your company logo or brand icon.                                                                                                     |
| `botImage`             | `string`                                                       | —                  | No            | URL for the chatbot avatar.                                                                                                                  |
| `initialChatText`      | `string`                                                       | —                  | No            | A greeting or tagline shown in the chat panel initially.                                                                                     |
| `width`                | `number \| string`                                             | `400`              | No            | Width of the help center (px or string).                                                                                                     |
| `height`               | `number \| string`                                             | `600`              | No            | Height of the help center (px or string).                                                                                                    |
| `fixed`                | `boolean`                                                      | `true`             | No            | Whether the help center is rendered as a fixed overlay. If `false`, it will take up available space.                                         |
| `open`                 | `boolean`                                                      | `false`            | No            | Initial open/close state of the help center.                                                                                                 |
| `initialView`          | `"home" \| "messages" \| "help" \| "walkthroughs"`             | `"home"`           | No            | Which tab is displayed when the help center is first opened. The "walkthroughs" option is only available when `enableOnboarding` is `true`. |
| `position`             | `"bottom-right" \| "bottom-left" \| "top-right" \| "top-left"` | `"bottom-right"`   | No            | Where the floating button and help center appear on the screen.                                                                              |
| `buttonRoundedCorners` | `string`                                                       | `"rounded-full"`   | No            | Tailwind classes for the main widget button shape.                                                                                           |
| `animated`             | `boolean`                                                      | `false`            | No            | If `true`, the help center has small animations on open/close.                                                                               |
| `customStyles`         | `React.CSSProperties`                                          | —                  | No            | Additional inline styles for the help center container.                                                                                      |
| `onOpenChange`         | `(open: boolean) => void`                                      | —                  | No            | Callback triggered whenever the help center is opened or closed.                                                                             |
| `enableOnboarding`     | `boolean`                                                      | `false`            | No            | Adds an onboarding dashboard ("Walkthroughs" tab) and allows the chatbot to create new flows on demand.                                      |
| `walkthroughOverrideDomainUrl` | `string`                                               | -                  | No            | Allows you to override the base url for walkthroughs. Useful if you want to test on local or staging environments.                           |

---

## Handling JWT Authentication

### Token Storage

- We only store the short-lived JWT **in memory**.
- **No** localStorage or sessionStorage usage.

### `onTokenExpired` Callback

- If your server returns a `401` or token is deemed invalid, we call `onTokenExpired`.
- You can then refresh the token, redirect to login, or log the user out.

### Token Derivation

By default, we extract `orgId` and basic user info from your JWT with:

```ts
import { extractUserFromToken } from "@sammy-labs/success";
```

But you can override these by explicitly passing `orgId` and `user`.

---

## Advanced Usage

1. **Text-to-Speech (TTS)**  
   Built-in TTS support can be toggled by the user or code. Messages from the AI can be spoken aloud.

2. **Image Attachments**  
   Chat UI supports uploading images, which are then sent to your `baseUrl` for processing.

3. **Walkthrough & Element Highlighting**  
   AI can produce an in-app step-by-step guide. We use advanced element matching, including XPath, attribute matching, and fallback logic.

4. **Live Chat Escalation**  
   Real-time chat with a human agent is supported if your backend is configured for live chat (via websockets/Socket.io).

5. **Docs & Help Center**  
   A dynamic documents viewer is included, with embeddings-based search and markdown rendering.

6. **Onboarding Dashboard & Chat-Generated Walkthroughs**  
   Enable `enableOnboarding` to surface a "Walkthroughs" tab that tracks each user's progress and lets the chatbot spin up new interactive guides automatically.

---

## License

MIT License - See [LICENSE](./LICENSE) for details.

```
    __
___( o >
\     )
 `---'  SAMMY LABS
```

## Theming

The Sammy package now supports custom theming through CSS variables. You can customize the colors of the UI by passing a `themeColors` prop to the `SammyProvider` component.

### Example

```tsx
import { SammyProvider } from "@sammy-labs/success";

function App() {
  return (
    <SammyProvider
      token="your-token"
      baseUrl="your-base-url"
      initialChatText="How can I help you today?"
      theme="light" // or "dark"
      themeColors={{
        primary: "#FF0000", // Main brand color
        secondary: "#0000FF", // Secondary color
        accent: "#00FF00", // Accent color for highlights
        error: "#FF0000", // Error messages and indicators
        success: "#00FF00", // Success messages and indicators
        background: "#FFFFFF", // Background color
        foreground: "#000000", // Text color
        border: "#CCCCCC", // Border color
      }}
    >
      {/* Your app content */}
    </SammyProvider>
  );
}
```

### Available Theme Colors

| Property   | Description                     | Default Value |
| ---------- | ------------------------------- | ------------- |
| primary    | Main brand color                | #F84000       |
| secondary  | Secondary color                 | #0074D9       |
| accent     | Accent color for highlights     | #FFC107       |
| error      | Error messages and indicators   | #DC2626       |
| success    | Success messages and indicators | #16A34A       |
| background | Background color                | #FFFFFF       |
| foreground | Text color                      | #111111       |
| border     | Border color                    | #E2E8F0       |

### Dark Mode

The package also supports a dark mode theme. You can enable it by setting the `theme` prop to `"dark"`:

```tsx
<SammyProvider
  token="your-token"
  baseUrl="your-base-url"
  initialChatText="How can I help you today?"
  theme="dark"
  themeColors={{
    // Optional: Override dark mode colors
    primary: "#FF6B35",
  }}
>
  {/* Your app content */}
</SammyProvider>
```

In dark mode, the default colors are automatically adjusted for better contrast and readability.