> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vizochok.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Examples

> Integration examples for common frameworks and scenarios

## Basic Floating Widget

The simplest integration -- a floating chat bubble in the bottom-right corner:

```html theme={null}
<script src="https://cdn.jsdelivr.net/npm/@vizochok/widget@0.1.2/dist/vizochok-widget.umd.js"></script>
<script>
  const widget = new VizochokWidget({
    apiKey: 'pk_your_public_key',
    storeId: 'your-store-id',
  });
  widget.mount();
</script>
```

## Inline Embedded Widget

Embed the chat directly inside a page element instead of using the floating bubble. The widget fills its container and hides the toggle button.

```html theme={null}
<div id="chat-container" style="width: 400px; height: 600px;"></div>

<script src="https://cdn.jsdelivr.net/npm/@vizochok/widget@0.1.2/dist/vizochok-widget.umd.js"></script>
<script>
  const widget = new VizochokWidget({
    apiKey: 'pk_your_public_key',
    storeId: 'your-store-id',
    botName: 'Shopping Assistant',
  });

  const container = document.getElementById('chat-container');
  widget.mount(container, { inline: true });
</script>
```

In inline mode:

* The toggle button is hidden
* The chat panel fills 100% of the container
* The close button in the header is hidden
* The chat is automatically opened on mount

## React Component Wrapper

Wrap the widget in a React component with proper lifecycle management:

```tsx theme={null}
import { useEffect, useRef } from 'react';
import { VizochokWidget } from '@vizochok/widget';
import type { CartChangedEvent } from '@vizochok/widget';

interface Props {
  apiKey: string;
  storeId: string;
  userId?: string;
  onCartUpdate?: (cart: CartChangedEvent) => void;
  inline?: boolean;
}

export function VizochokChat({
  apiKey,
  storeId,
  userId,
  onCartUpdate,
  inline,
}: Props) {
  const containerRef = useRef<HTMLDivElement>(null);
  const widgetRef = useRef<VizochokWidget | null>(null);

  useEffect(() => {
    const widget = new VizochokWidget({
      apiKey,
      storeId,
      userId,
      language: 'en',
      theme: {
        primaryColor: '#1a73e8',
        borderRadius: '12px',
        mode: 'auto',
      },
      onCartChanged: (cart) => onCartUpdate?.(cart),
      onError: (error) => {
        console.error('VIZOCHOK error:', error.code, error.message);
      },
    });

    widgetRef.current = widget;

    if (inline && containerRef.current) {
      widget.mount(containerRef.current, { inline: true });
    } else {
      widget.mount();
    }

    return () => {
      widget.unmount();
    };
  }, [apiKey, storeId, userId]);

  if (inline) {
    return (
      <div ref={containerRef} style={{ width: '100%', height: '100%' }} />
    );
  }

  return null; // Floating mode -- mounts on document.body
}
```

Usage:

```tsx theme={null}
function StorePage() {
  const [cart, setCart] = useState([]);

  return (
    <div>
      <ProductList />
      <VizochokChat
        apiKey="pk_your_key"
        storeId="your-store"
        userId={currentUser?.id}
        onCartUpdate={(event) => setCart(event.items)}
      />
    </div>
  );
}
```

Inline in a sidebar:

```tsx theme={null}
function ProductPage() {
  return (
    <div style={{ display: 'flex' }}>
      <main style={{ flex: 1 }}>
        {/* Product content */}
      </main>
      <aside style={{ width: 400, height: '100vh' }}>
        <VizochokChat
          apiKey="pk_your_key"
          storeId="your-store"
          inline
        />
      </aside>
    </div>
  );
}
```

## Vue Component Wrapper

Wrap the widget in a Vue 3 Composition API component:

```vue theme={null}
<template>
  <div v-if="inline" ref="containerRef" class="vizochok-container" />
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { VizochokWidget } from '@vizochok/widget';
import type { CartChangedEvent } from '@vizochok/widget';

const props = defineProps<{
  apiKey: string;
  storeId: string;
  userId?: string;
  inline?: boolean;
}>();

const emit = defineEmits<{
  cartChanged: [cart: CartChangedEvent];
}>();

const containerRef = ref<HTMLDivElement>();
let widget: VizochokWidget | null = null;

onMounted(() => {
  widget = new VizochokWidget({
    apiKey: props.apiKey,
    storeId: props.storeId,
    userId: props.userId,
    language: 'en',
    theme: {
      primaryColor: '#2D8A4E',
      borderRadius: '12px',
      mode: 'auto',
    },
    onCartChanged: (cart) => emit('cartChanged', cart),
  });

  if (props.inline && containerRef.value) {
    widget.mount(containerRef.value, { inline: true });
  } else {
    widget.mount();
  }
});

onBeforeUnmount(() => {
  widget?.unmount();
  widget = null;
});
</script>

<style scoped>
.vizochok-container {
  width: 100%;
  height: 600px;
}
</style>
```

Usage:

```vue theme={null}
<template>
  <VizochokChat
    api-key="pk_your_key"
    store-id="your-store"
    :user-id="currentUser?.id"
    @cart-changed="handleCartChanged"
  />
</template>
```

## Cart Synchronization with Store

A complete example showing cart sync between the AI assistant and your store:

```typescript theme={null}
import { VizochokWidget } from '@vizochok/widget';

const widget = new VizochokWidget({
  apiKey: 'pk_your_key',
  storeId: 'your-store',
  userId: getCurrentUserId(),

  // AI changes cart -> update store
  onCartChanged: (cart) => {
    // Update your cart state
    storeCart.replaceItems(cart.items.map(item => ({
      sku: item.sku,
      name: item.name,
      price: item.promo_price ?? item.price,
      quantity: item.quantity,
      brand: item.brand,
    })));

    storeCart.setTotal(cart.total);
    renderCartUI();

    // Show notification
    switch (cart.action) {
      case 'item_selected':
        showToast(`${cart.name} added to cart`);
        break;
      case 'item_removed':
        showToast(`${cart.name} removed from cart`);
        break;
      case 'quantity_updated':
        showToast(`${cart.name} updated to ${cart.quantity}`);
        break;
      case 'cart_cleared':
        showToast('Cart cleared');
        break;
    }
  },

  // Restore cart on reconnect
  onSessionComplete: (summary) => {
    if (summary.item_count > 0) {
      storeCart.replaceItems(summary.selected_items);
      storeCart.setTotal(summary.total);
      renderCartUI();
    }
  },
});

widget.mount();
```

<Info>
  The initial cart is loaded from your backend via the `cart_get_url` webhook when a new conversation starts. See the [Cart Webhook](/webhooks/cart) documentation for server-side setup.
</Info>

## Custom Styling

Apply different visual themes by combining config options:

### Dark E-commerce Theme

```typescript theme={null}
const widget = new VizochokWidget({
  apiKey: 'pk_your_key',
  storeId: 'your-store',
  botName: 'Shopping AI',
  botAvatar: 'https://cdn.example.com/bot.png',
  welcomeMessage: 'Hi! I can help you find products and build your cart.',
  placeholder: 'What are you looking for?',
  position: 'bottom-right',
  theme: {
    primaryColor: '#6366f1',
    fontFamily: "'Inter', system-ui, sans-serif",
    borderRadius: '16px',
    mode: 'dark',
    currency: '$',
  },
});
```

### Minimal Light Theme

```typescript theme={null}
const widget = new VizochokWidget({
  apiKey: 'pk_your_key',
  storeId: 'your-store',
  botName: 'Assistant',
  position: 'bottom-left',
  theme: {
    primaryColor: '#000000',
    fontFamily: "'Georgia', serif",
    borderRadius: '0px',
    mode: 'light',
    currency: 'EUR',
  },
});
```

## Multi-Language Setup

The `language` option controls the language of widget UI text (errors, status messages):

```typescript theme={null}
// Ukrainian (default)
const widgetUk = new VizochokWidget({
  apiKey: 'pk_your_key',
  storeId: 'your-store',
  language: 'uk',
  botName: 'Помічник',
  placeholder: 'Що шукаєте?',
  welcomeMessage: 'Привіт! Я допоможу зі списком покупок.',
});

// English
const widgetEn = new VizochokWidget({
  apiKey: 'pk_your_key',
  storeId: 'your-store',
  language: 'en',
  botName: 'Assistant',
  placeholder: 'What are you looking for?',
  welcomeMessage: 'Hi! I can help you build your shopping list.',
});
```

<Info>
  The `language` setting controls widget UI text only (errors, status messages). The AI assistant's response language is determined by the user's input -- Ukrainian/Russian input gets Ukrainian responses, other languages get responses in that language.
</Info>

The widget includes built-in translations for:

* 12 error codes
* 15 status codes (searching, thinking, adding to cart, etc.)

Russian (`'ru'`) uses the Ukrainian translations.

## Switching Between Inline and Floating

Use `setMode()` to switch the widget between inline and floating modes without reconnecting:

```typescript theme={null}
const widget = new VizochokWidget({
  apiKey: 'pk_your_key',
  storeId: 'your-store',
});
widget.mount();

// Switch to inline when user navigates to a chat page
const chatContainer = document.getElementById('chat-page');
widget.setMode('inline', chatContainer);

// Switch back to floating when user leaves
widget.setMode('floating');
```

## Programmatic Control

Control the widget from external buttons or triggers:

```typescript theme={null}
const widget = new VizochokWidget({
  apiKey: 'pk_your_key',
  storeId: 'your-store',
});
widget.mount();

// Open chat from a custom button
document.getElementById('help-btn').addEventListener('click', () => {
  widget.open();
});

// Start a new conversation
document.getElementById('new-chat-btn').addEventListener('click', () => {
  widget.newChat();
});

// Clean up on SPA page transition (preserves session)
function onPageLeave() {
  widget.unmount();
}

// Full cleanup when user logs out (clears session)
function onLogout() {
  widget.destroy();
}
```

## Full Production Example

A complete production-ready integration:

```typescript theme={null}
import { VizochokWidget } from '@vizochok/widget';

let widget: VizochokWidget | null = null;

export function initAssistant(config: {
  userId?: string;
  language: 'uk' | 'en' | 'ru';
}) {
  widget?.destroy();

  widget = new VizochokWidget({
    apiKey: import.meta.env.VITE_VIZOCHOK_KEY,
    storeId: import.meta.env.VITE_STORE_ID,
    userId: config.userId,
    language: config.language,

    theme: {
      primaryColor: getComputedStyle(document.documentElement)
        .getPropertyValue('--brand-primary')
        .trim(),
      fontFamily: getComputedStyle(document.body).fontFamily,
      borderRadius: '12px',
      mode: 'auto',
      currency: '$',
    },

    botName: config.language === 'uk' ? 'Помічник' : 'Assistant',

    onCartChanged: (cart) => {
      window.dispatchEvent(
        new CustomEvent('cart:update', { detail: cart })
      );
    },

    onProductClick: (product) => {
      window.location.href = `/product/${product.sku}`;
    },

    onError: (error) => {
      if (!['ws_error', 'connection_lost'].includes(error.code)) {
        errorReporter.report(error.code, error.message);
      }
    },
  });

  widget.mount();
}

export function destroyAssistant() {
  widget?.destroy();
  widget = null;
}
```
