npm package: @rodgerai/widgets
Description: Pre-built React widgets for Rodger AI tools
Author: William Kasel
Latest version: 2.0.0

# @rodger/widgets

Beautiful, production-ready React widgets for Rodger AI tools. Seamlessly integrate with @assistant-ui/react to provide rich, interactive UI experiences for tool results.

## Features

- 5 Pre-built Widgets - Cart builder, product lookup, quick actions, email signup, CTA buttons
- Built on assistant-ui - Leverages powerful assistant-ui primitives
- Type-Safe - Full TypeScript support with proper interfaces
- Tailwind Styled - Easy customization with Tailwind CSS
- State Management - Handles loading, success, error states automatically
- Responsive Design - Works on mobile, tablet, and desktop

## Installation

```bash
npm install @rodger/widgets
# or
pnpm add @rodger/widgets
```

## Peer Dependencies

```json
{
  "@assistant-ui/react": "^0.11.0",
  "react": "^18.0.0 || ^19.0.0"
}
```

These are automatically installed if not present.

## Quick Start

### 1. Set Up Backend with Tools

```typescript
// app/api/chat/route.ts
import { createAgent } from '@rodger/core';
import { cartBuilder, productLookup } from '@rodger/tools';

const agent = createAgent({
  name: 'Shopping Assistant',
  llm: { provider: 'openai', model: 'gpt-4o' },
  tools: { cartBuilder, productLookup }
});
```

### 2. Register Widgets in Frontend

```tsx
// components/Chat.tsx
'use client';

import { Thread } from '@rodger/ui';
import { CartBuilderUI, ProductLookupUI } from '@rodger/widgets';

export default function Chat() {
  return (
    <Thread
      widgets={{
        cartBuilder: CartBuilderUI,
        productLookup: ProductLookupUI
      }}
    />
  );
}
```

That's it! When your agent uses these tools, the widgets automatically render the results.

## Available Widgets

### 1. CartBuilderUI

Interactive cart builder with approval workflow, loading states, and checkout links.

```tsx
import { CartBuilderUI } from '@rodger/widgets';

<Thread
  widgets={{
    cartBuilder: CartBuilderUI
  }}
/>
```

**Features:**
- Preview state with product details
- User approval workflow
- Loading state during cart creation
- Success state with checkout link
- Error handling with retry options
- Animated transitions

**Tool Result Interface:**
```typescript
interface CartBuilderResult {
  requiresApproval?: boolean;
  preview?: {
    products: Array<{
      entityId: number;
      name: string;
      quantity: number;
      price?: number;
    }>;
    protocolName: string;
    notes: string | null;
  };
  message?: string;
}
```

**States:**
1. **Preview** - Shows cart contents, requires approval
2. **Loading** - Displays spinner during cart creation
3. **Success** - Shows checkout link and confirmation
4. **Error** - Shows error message with retry option

**Pairs with:** `cartBuilder` from @rodger/tools

### 2. ProductLookupUI

Display product search results as interactive cards.

```tsx
import { ProductLookupUI } from '@rodger/widgets';

<Thread
  widgets={{
    productLookup: ProductLookupUI
  }}
/>
```

**Features:**
- Responsive card grid layout
- Product metadata display (name, price, size)
- External product page links
- Disclaimer for research products
- Hover effects and animations

**Tool Result Format:**
```
ID: 123 | Product Name (Nickname) | $99.99 | 30 servings | https://example.com/product
ID: 456 | Another Product | $79.99 | 60 capsules | https://example.com/product2
```

**Component Exports:**
```typescript
import {
  ProductLookupUI,      // Main widget
  ProductCard,          // Individual card component
  parseProductList      // Helper to parse tool results
} from '@rodger/widgets';
```

**Pairs with:** `productLookup` from @rodger/tools

### 3. QuickActionsWidget

Display interactive button choices to guide conversations.

```tsx
import { QuickActionsWidget } from '@rodger/widgets';

<Thread
  widgets={{
    quickActions: QuickActionsWidget
  }}
/>
```

**Features:**
- Grid layout for multiple buttons
- Click to send pre-filled prompts
- Hover effects
- Responsive design (1-2 columns)
- Integrated with ThreadPrimitive.Suggestion

**Tool Result Interface:**
```typescript
{
  message: string;           // Message above buttons
  actions: Array<{
    label: string;           // Button text
    prompt: string;          // Prompt to send when clicked
  }>;
}
```

**Example Rendering:**
```
What would you like to do?
[Browse Products] [Get Recommendations]
```

**Pairs with:** `quickActions` from @rodger/tools

### 4. EmailSignupWidget

Collect user email addresses with validation and feedback.

```tsx
import { EmailSignupWidget } from '@rodger/widgets';

<Thread
  widgets={{
    emailSignup: EmailSignupWidget
  }}
/>
```

**Features:**
- Success/error state handling
- Visual feedback with icons
- Animated transitions
- Clean, minimal design
- Email validation messages

**Tool Result Interface:**
```typescript
{
  success: boolean;
  email: string | null;
  message: string;          // Feedback message
}
```

**States:**
1. **Success** - Shows checkmark and confirmation
2. **Error** - Shows error icon and message

**Pairs with:** `emailSignup` from @rodger/tools

### 5. CtaButtonWidget

Display prominent call-to-action buttons for important actions.

```tsx
import { CtaButtonWidget } from '@rodger/widgets';

<Thread
  widgets={{
    ctaButton: CtaButtonWidget
  }}
/>
```

**Features:**
- Primary and secondary variants
- Optional description text
- External link with icon
- Hover animations
- Calendar icon for booking actions
- Opens in new window

**Tool Result Interface:**
```typescript
{
  label: string;             // Button text
  url: string;               // Link destination
  description?: string;      // Optional description
  variant?: 'primary' | 'secondary'; // Button style
}
```

**Variants:**
- **Primary** - Navy background with light text
- **Secondary** - Border style with hover fill

**Pairs with:** `ctaButton` from @rodger/tools

## Usage Patterns

### Register All Widgets

```tsx
import { Thread } from '@rodger/ui';
import {
  CartBuilderUI,
  ProductLookupUI,
  QuickActionsWidget,
  EmailSignupWidget,
  CtaButtonWidget
} from '@rodger/widgets';

<Thread
  widgets={{
    cartBuilder: CartBuilderUI,
    productLookup: ProductLookupUI,
    quickActions: QuickActionsWidget,
    emailSignup: EmailSignupWidget,
    ctaButton: CtaButtonWidget
  }}
/>
```

### Selective Registration

Only register widgets for tools your agent uses:

```tsx
// Agent only uses cartBuilder and productLookup
<Thread
  widgets={{
    cartBuilder: CartBuilderUI,
    productLookup: ProductLookupUI
  }}
/>
```

### Using Individual Components

Export and use sub-components directly:

```tsx
import { ProductCard, parseProductList } from '@rodger/widgets';

function MyCustomProductDisplay({ toolResult }) {
  const products = parseProductList(toolResult);

  return (
    <div className="grid grid-cols-3 gap-4">
      {products.map((product, idx) => (
        <ProductCard key={idx} product={product} />
      ))}
    </div>
  );
}
```

### Lazy Loading

Load widgets dynamically:

```tsx
import { widgets } from '@rodger/widgets';

// Lazy load widgets
const CartBuilderUI = await widgets.cartBuilder();
const ProductLookupUI = await widgets.productLookup();

<Thread
  widgets={{
    cartBuilder: CartBuilderUI,
    productLookup: ProductLookupUI
  }}
/>
```

## Styling and Theming

### Tailwind Configuration

Add @rodger/widgets to your Tailwind config:

```js
// tailwind.config.js
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx}',
    './node_modules/@rodger/widgets/dist/**/*.{js,mjs}'
  ],
  theme: {
    extend: {
      colors: {
        'navy-dark': '#1a202c',
        'warm-beige': '#f5f1e8',
        background: 'hsl(var(--background))',
        foreground: 'hsl(var(--foreground))',
        'muted-foreground': 'hsl(var(--muted-foreground))'
      }
    }
  }
};
```

### CSS Variables

Define theme colors in your CSS:

```css
/* app/globals.css */
:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --muted-foreground: 215.4 16.3% 46.9%;
}

.dark {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 98%;
  --muted-foreground: 215 20.2% 65.1%;
}
```

### Custom Styling

Override widget styles with Tailwind classes:

```tsx
// Custom cart builder styling
<div className="[&_.cart-preview]:bg-blue-100 [&_.cart-button]:bg-blue-600">
  <Thread widgets={{ cartBuilder: CartBuilderUI }} />
</div>
```

Or use custom CSS:

```css
/* Custom widget styles */
.cart-preview {
  @apply rounded-xl shadow-lg;
}

.product-card {
  @apply hover:scale-105 transition-transform;
}
```

## Creating Custom Widgets

Build your own widgets using the assistant-ui patterns:

### Basic Widget

```tsx
// MyWidget.tsx
import { makeAssistantToolUI } from '@assistant-ui/react';

export const MyWidget = makeAssistantToolUI({
  toolName: 'myTool',
  render: function MyWidgetContent({ result }) {
    const data = result as { message: string };

    return (
      <div className="my-4 rounded-lg bg-background p-4">
        <p>{data.message}</p>
      </div>
    );
  }
});
```

### Widget with State Management

```tsx
import { makeAssistantToolUI } from '@assistant-ui/react';
import { useState } from 'react';

export const InteractiveWidget = makeAssistantToolUI({
  toolName: 'interactiveTool',
  render: function Content({ result }) {
    const [isApproved, setIsApproved] = useState(false);

    if (isApproved) {
      return <div>Approved!</div>;
    }

    return (
      <div>
        <button onClick={() => setIsApproved(true)}>
          Approve
        </button>
      </div>
    );
  }
});
```

### Widget with API Calls

```tsx
import { makeAssistantToolUI } from '@assistant-ui/react';
import { useState } from 'react';

export const ApiWidget = makeAssistantToolUI({
  toolName: 'apiTool',
  render: function Content({ result }) {
    const [status, setStatus] = useState<'idle' | 'loading' | 'success'>('idle');

    const handleAction = async () => {
      setStatus('loading');
      await fetch('/api/action', {
        method: 'POST',
        body: JSON.stringify(result)
      });
      setStatus('success');
    };

    return (
      <div>
        {status === 'loading' && <div>Loading...</div>}
        {status === 'success' && <div>Success!</div>}
        {status === 'idle' && (
          <button onClick={handleAction}>
            Take Action
          </button>
        )}
      </div>
    );
  }
});
```

### Best Practices

1. **Type Safety** - Define interfaces for tool results:

```typescript
interface MyToolResult {
  data: string;
  metadata?: Record<string, unknown>;
}

render: ({ result }) => {
  const data = result as MyToolResult;
  // Full type safety
}
```

2. **Error Handling** - Handle missing or malformed data:

```typescript
render: ({ result }) => {
  if (!result || typeof result !== 'object') {
    return <div>Invalid tool result</div>;
  }
  // Render normally
}
```

3. **Loading States** - Show feedback during async operations:

```typescript
const [isLoading, setIsLoading] = useState(false);

const handleAction = async () => {
  setIsLoading(true);
  try {
    await performAction();
  } finally {
    setIsLoading(false);
  }
};
```

4. **Accessibility** - Use semantic HTML and ARIA attributes:

```tsx
<button
  onClick={handleAction}
  aria-label="Approve cart"
  disabled={isLoading}
>
  Approve
</button>
```

5. **Responsive Design** - Use Tailwind responsive classes:

```tsx
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  {/* Responsive grid */}
</div>
```

## TypeScript Support

Full TypeScript support with exported types:

```typescript
import type {
  CartPreview,
  Product
} from '@rodger/widgets';
```

## Complete Example

```tsx
// app/page.tsx
'use client';

import { RodgerProvider, Thread, useRodgerChat } from '@rodger/ui';
import {
  CartBuilderUI,
  ProductLookupUI,
  QuickActionsWidget,
  EmailSignupWidget,
  CtaButtonWidget
} from '@rodger/widgets';

export default function ShoppingChat() {
  const { runtime, isLoadingHistory } = useRodgerChat({
    endpoint: '/api/chat',
    sessionId: 'shopping-session'
  });

  if (isLoadingHistory) {
    return <div>Loading...</div>;
  }

  return (
    <RodgerProvider runtime={runtime}>
      <div className="h-screen">
        <Thread
          welcomeMessage={
            <div>
              <h1 className="text-4xl font-bold">Shopping Assistant</h1>
              <p className="text-muted-foreground">
                Browse products and build your cart
              </p>
            </div>
          }
          widgets={{
            cartBuilder: CartBuilderUI,
            productLookup: ProductLookupUI,
            quickActions: QuickActionsWidget,
            emailSignup: EmailSignupWidget,
            ctaButton: CtaButtonWidget
          }}
        />
      </div>
    </RodgerProvider>
  );
}
```

## Related Packages

- **[@rodger/core](../core)** - Agent framework (required for backend)
- **[@rodger/tools](../tools)** - Pre-built tools (pairs with widgets)
- **[@rodger/ui](../ui)** - React components for chat UIs

## Documentation

- [Full Documentation](https://docs.rodger.ai)
- [Creating Custom Widgets](https://docs.rodger.ai/widgets/creating-widgets)
- [Widget Integration Guide](https://docs.rodger.ai/core-concepts/widgets)

## License

MIT