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

# Salesive Widget

> Embeddable widgets automatically included in all Salesive storefronts

The Salesive Widget system provides embeddable UI components for Salesive storefronts. All widgets use Shadow DOM for complete style isolation and expose a global `SalesiveWidget` API for programmatic control.

<Note>
  The widget script (`https://www.salesive.shop/widget.js`) is automatically
  injected and initialized in all Salesive storefronts. No manual setup is
  required.
</Note>

## Available widgets

<CardGroup cols={1}>
  <Card title="Live Support Chat" icon="comment" href="#live-support-chat">
    Real-time chat widget connecting customers with your sales and support
    team.
  </Card>

  <Card title="Push Notifications" icon="bell" href="#push-notifications">
    Subscribe users to real-time order updates and support messages.
  </Card>

  <Card title="PWA Installation" icon="mobile" href="#pwa-installation">
    Seamlessly guide users to add your store to their home screen.
  </Card>

  <Card title="Pending Reviews" icon="star" href="#pending-reviews">
    Slide-out panel for collecting product ratings and reviews from
    customers.
  </Card>
</CardGroup>

***

## Live Support Chat

The Live Support Chat widget provides real-time communication between customers and your team. It supports route-based visibility, dark mode, and preserves chat history across page navigations.

### Default behavior

The chat widget initializes automatically with these defaults:

* Appears on all pages (no route filtering)
* Light mode enabled
* Shadow DOM isolation active
* Bundled CSS auto-injected

Use the API methods below to customize behavior at runtime.

### Sessions and sign-in

The widget manages the shopper's chat identity on its own:

* On first load it creates an anonymous **guest session**, so a visitor can
  start chatting without signing in.
* When the shopper signs in through the storefront, the widget detects it
  within about a second, reconnects its socket under the real account, and
  stops showing the sign-in prompt. This also works the other way on logout.

No integration work is required — your theme does not need to reload the page,
call `unmount()`/`init()`, or notify the widget after login. Conversations
started after sign-in are attached to the shopper's account rather than the
guest session.

### API methods

The `SalesiveWidget` global object exposes the following methods for the chat widget:

#### chat.open()

Open the chat window (expand from bubble).

```javascript theme={null}
SalesiveWidget.chat.open();
```

#### chat.close()

Close the chat window (minimize to bubble).

```javascript theme={null}
SalesiveWidget.chat.close();
```

#### chat.toggle()

Toggle the chat window state (open/close).

```javascript theme={null}
SalesiveWidget.chat.toggle();
```

#### chat.isOpened (property)

Check if the chat window is currently open. Returns `true` if open, `false` otherwise.

```javascript theme={null}
if (SalesiveWidget.chat.isOpened) {
    console.log("Chat is open");
}
```

#### chat.hide()

Hide the widget while keeping it mounted. Preserves chat state and history.

```javascript theme={null}
SalesiveWidget.chat.hide();
```

#### chat.show()

Show the widget if it was previously hidden.

```javascript theme={null}
SalesiveWidget.chat.show();
```

#### chat.toggleVisibility()

Toggle widget visibility. Returns the new visibility state.

```javascript theme={null}
const isVisible = SalesiveWidget.chat.toggleVisibility();
console.log("Widget visible:", isVisible);
```

#### chat.setRoutes(routes)

Update the route filtering dynamically.

```javascript theme={null}
// Show only on specific pages
SalesiveWidget.chat.setRoutes(["/checkout", "/cart"]);

// Show on all pages
SalesiveWidget.chat.setRoutes(null);
```

#### chat.setDarkMode(enabled)

Toggle or set dark mode. The preference persists across page loads.

```javascript theme={null}
// Enable dark mode
SalesiveWidget.chat.setDarkMode(true);

// Toggle dark mode
SalesiveWidget.chat.setDarkMode();
```

#### chat.isVisible (property)

Read-only property to check current visibility state.

```javascript theme={null}
if (SalesiveWidget.chat.isVisible) {
    console.log("Widget is visible");
}
```

#### chat.prefill(options)

Prefill the chat input with a text message and/or an image URL. This opens the chat window, switches to the current store's conversation, and populates the input — ready for the user to review and send.

```javascript theme={null}
// Prefill text
SalesiveWidget.chat.prefill({ text: "I need help with my order" });

// Prefill an image (must be an already-accessible URL)
SalesiveWidget.chat.prefill({ imageUrl: "https://example.com/screenshot.png" });

// Both text and image
SalesiveWidget.chat.prefill({
    text: "This product arrived damaged",
    imageUrl: "https://example.com/photo.jpg",
});
```

| Property   | Type     | Description                                      |
| ---------- | -------- | ------------------------------------------------ |
| `text`     | `string` | Text to place in the chat input                  |
| `imageUrl` | `string` | URL of an image to attach (displayed as preview) |

<Tip>
  Use this to create contextual "Contact support" buttons on product or order
  pages that pre-populate the chat with relevant details.
</Tip>

#### chat.on(event, handler)

Listen for real-time socket events from the chat system.

```javascript theme={null}
SalesiveWidget.chat.on("message:new", (message) => {
    console.log("New message received:", message);
});
```

<Warning>
  Not every message is from a person. Conversation threads also carry
  **system notes** (`type: "system"` — e.g. "Ada took over this
  conversation") and **call log entries** (`type: "call"`), and both have
  `sender: null`. Guard before dereferencing — `message.sender?._id`, never
  `message.sender._id`. Remember that `typeof null === "object"`, so a
  `typeof` check alone does not protect you.
</Warning>

#### chat.emit(event, data)

Send real-time events to the chat system.

```javascript theme={null}
SalesiveWidget.chat.emit("user:typing", { conversationId: "123" });
```

<Note>
  Previous global methods `hide()`, `show()`, `toggle()`, `setRoutes()`, and
  `setDarkMode()` are now deprecated but still available for backward
  compatibility. Please update your integration to use the `chat` namespace.
</Note>

#### setDebugMode(enabled)

Enable or disable debug logging to the browser console.

```javascript theme={null}
SalesiveWidget.setDebugMode(true);
```

The current widget version string.

```javascript theme={null}
console.log(SalesiveWidget.version); // "1.0.0"
```

### Route filtering

Control which pages display the widget using the `routes` option. Routes use smart matching:

* **`"/"`** — Matches only the exact homepage, not sub-paths
* **Other routes** — Use prefix matching with path boundary detection

```javascript theme={null}
// Show on homepage only
SalesiveWidget.chat.setRoutes(["/"]);

// Show on /shop and all sub-paths (/shop/item, /shop/category/shoes)
// Does NOT match /shopping or /shops
SalesiveWidget.chat.setRoutes(["/shop"]);

// Show on multiple sections
SalesiveWidget.chat.setRoutes(["/", "/shop", "/support"]);
```

<Note>
  Route filtering works with single-page applications. The widget listens to
  `popstate` events and intercepts `pushState`/`replaceState` calls to detect
  navigation changes.
</Note>

### Visibility control

Use `chat.hide()`, `chat.show()`, and `chat.toggleVisibility()` to control visibility without losing chat state:

```javascript theme={null}
// Hide widget during checkout flow
function startCheckout() {
    SalesiveWidget.chat.hide();
}

// Show widget after checkout
function completeCheckout() {
    SalesiveWidget.chat.show();
}

// Toggle with a custom button
document.getElementById("toggle-chat").addEventListener("click", () => {
    SalesiveWidget.chat.toggleVisibility();
});
```

<Tip>
  Use `hide()`/`show()` instead of `unmount()`/`init()` when you want to
  preserve the user's chat session and message history.
</Tip>

### Dark mode

Dark mode can be toggled dynamically:

```javascript theme={null}
// Toggle based on user preference
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
SalesiveWidget.chat.setDarkMode(prefersDark);

// Listen for system preference changes
window
    .matchMedia("(prefers-color-scheme: dark)")
    .addEventListener("change", (e) => {
        SalesiveWidget.chat.setDarkMode(e.matches);
    });
```

<Note>
  Dark mode preference is automatically persisted to localStorage and restored
  on subsequent page loads.
</Note>

### Shadow DOM isolation

The widget renders inside a Shadow DOM to prevent style conflicts with the host page:

* Widget styles don't leak into the host page
* Host page styles don't affect the widget
* Complete encapsulation of the widget UI

### Theming

Customize the widget's primary and secondary brand colors at runtime or before initialization.

#### Pre-init theming (recommended)

Set `window.SalesiveWidgetConfig` **before** the widget script loads to apply your brand colors on first render with no flash of default colors:

```html theme={null}
<script>
    window.SalesiveWidgetConfig = {
        theme: {
            primary: "#7c3aed",
            secondary: "goldenrod",
            dark: "#1a1a2e",
        },
    };
</script>
<script src="https://www.salesive.shop/widget.js"></script>
```

<Tip>
  This is the recommended approach for Salesive storefronts that use a custom
  brand color. It avoids the brief flash of the default blue before the theme
  is applied.
</Tip>

#### setTheme(theme)

Update the widget's theme colors at runtime. Changes apply instantly to all widget surfaces (chat, bubble, notification modal, PWA modal, reviews slider).

```javascript theme={null}
SalesiveWidget.setTheme({
    primary: "rebeccapurple",
    secondary: "gold",
    dark: "#1a1a2e", // Dark mode background color
});
```

| Property    | Type     | Description                                                                          |
| ----------- | -------- | ------------------------------------------------------------------------------------ |
| `primary`   | `string` | Any CSS color value (`#hex`, `rgb()`, `hsl()`, named colors)                         |
| `secondary` | `string` | Optional. Defaults to `primary` if omitted or set to `null`                          |
| `dark`      | `string` | Dark mode background color. Defaults to gray-900 (`#111827`). Light mode stays white |

Theme values are automatically **persisted to localStorage**, so custom colors survive page reloads without a flash of default colors.

<Note>
  If an invalid color string is passed, the widget logs a warning and keeps
  the previous value.
</Note>

#### getTheme()

Returns the currently applied theme values.

```javascript theme={null}
const theme = SalesiveWidget.getTheme();
console.log(theme); // { primary: "rebeccapurple", secondary: "gold", dark: "#1a1a2e" }
```

#### Theme via init options

You can also pass theme colors in the `init()` options:

```javascript theme={null}
SalesiveWidget.init({
    theme: { primary: "#ef4444", secondary: "#22c55e", dark: "#1a1a2e" },
});
```

***

### Example: custom controls

Add custom buttons to control the widget programmatically:

```javascript theme={null}
// Toggle button
document.getElementById("toggle-widget").addEventListener("click", () => {
    SalesiveWidget.toggle();
});

// Dark mode toggle
document.getElementById("dark-mode").addEventListener("click", () => {
    SalesiveWidget.setDarkMode();
});

// Configure route filtering
SalesiveWidget.setRoutes(["/", "/shop", "/support"]);

// Sync with system dark mode preference
window
    .matchMedia("(prefers-color-scheme: dark)")
    .addEventListener("change", (e) => {
        SalesiveWidget.setDarkMode(e.matches);
    });
```

***

### Voice calls

When a human agent from the store is online, the chat header shows a call
button and the shopper can start a voice call (WebRTC, browser-to-browser —
no phone number involved). Incoming calls from the store ring inside the
widget the same way.

#### Call API

Control calls programmatically from theme code via `SalesiveWidget.chat.call`.
Everything is safe to call blind: reads return inert defaults before the widget
initializes, and `start()`/`isEnabled()` resolve rather than reject. The
enabled flag is enforced server-side regardless — this API is UX, not
authorization.

```javascript theme={null}
const call = SalesiveWidget.chat.call;

await call.isEnabled();   // true | false | null (not determinable yet)

// Opens the chat and starts a voice call to the store.
const { started, reason } = await call.start();
// reason when not started: "disabled" | "busy" | "unavailable" | "timeout"

call.getState();          // { status, muted, speakerOn }
call.status;              // "idle" | "outgoing" | "connecting" | "connected" | "incoming"
call.toggleMute();
call.toggleSpeaker();     // loudspeaker vs earpiece (best-effort per platform)
call.hangup();

const unsubscribe = call.onStatusChange((status) => {
    console.log("call is now", status);
});
```

<Note>
  **Backgrounding mutes the microphone.** Mobile operating systems silence
  microphone capture for a browser tab or installed PWA that is not in the
  foreground — the call stays connected and incoming audio keeps playing,
  but the other side cannot hear the shopper until the app is foregrounded
  again. This is an OS restriction; no web page can override it. Advise
  shoppers to keep the store open during a call.
</Note>

## Push Notifications

The Push Notifications widget prompts the user to subscribe to order updates and support responses.

### API methods

The `SalesiveWidget` global object exposes the following methods for push notifications:

#### subscription.open()

Manually trigger the subscription prompt modal.

```javascript theme={null}
SalesiveWidget.subscription.open();
```

#### subscription.getSubscriptionCount()

Get the total number of notifications the user is subscribed to.

```javascript theme={null}
const count = SalesiveWidget.subscription.getSubscriptionCount();
console.log("Subscription count:", count);
```

***

## PWA Installation

The PWA Installation widget detects if the application is installable and prompts the user to add it to their home screen. It handles both standard browser prompts and custom iOS instructions.

### API methods

The `SalesiveWidget` global object exposes the following methods for PWA installation:

#### pwa.open()

Manually trigger the installation prompt modal. This is useful for "Install App" buttons in your UI.

```javascript theme={null}
SalesiveWidget.pwa.open();
```

#### pwa.isInstallable()

Check if the PWA is currently installable (i.e., if the browser has fired the `beforeinstallprompt` event or if it's an iOS device).

```javascript theme={null}
const canInstall = SalesiveWidget.pwa.isInstallable();
if (canInstall) {
    document.getElementById("install-button").style.display = "block";
}
```

***

## Pending Reviews

The Pending Reviews widget displays a slider containing products from recent orders that need to be reviewed. Users can rate products and leave comments directly within the widget.

### Features

* **Auto-show**: Automatically appears 10 seconds after page load if there are pending reviews.
* **Frequency control**: Once closed, it waits 24 hours before auto-showing again to avoid pestering users.
* **One-by-one flow**: Guides users through reviews sequentially.

### API methods

The `SalesiveWidget` global object exposes the following methods for pending reviews:

#### reviews.open()

Manually open the pending reviews slider. This bypasses the 24-hour delay check, making it suitable for "Write a Review" buttons or menu items.

```javascript theme={null}
SalesiveWidget.reviews.open();
```

#### reviews.close()

Programmatically close the reviews slider.

```javascript theme={null}
SalesiveWidget.reviews.close();
```
