---
title: "CDN / Тег script"
description: "Добавьте голосовой виджет ThunderPhone на любой сайт без сборщика"
---

Сборка CDN включает React, поэтому вы можете использовать виджет на статических сайтах, WordPress, Webflow или любой странице, где можно добавить HTML. Не нужны ни npm, ни сборщик, ни фреймворк.

## URL-адреса CDN

<CodeGroup>
```html Latest (recommended)
<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
```

```html Pinned version
<!-- Each stable release also publishes an immutable /widget/vX.Y.Z/ path
     matching the @thunderphone/widget npm version (v1.1.2 at the time of
     writing). If you pin, you own updating the URL for new releases. -->
<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/v1.1.2/style.css" />
<script src="https://cdn.thunderphone.com/widget/v1.1.2/widget.js"></script>
```
</CodeGroup>

<Tip>
  URL-адреса `latest` создаёт генератор сниппетов для встраивания в панели управления. Они кэшируются на 5 минут и автоматически получают новые стабильные релизы. Закреплённые пути `/widget/vX.Y.Z/` неизменяемы и кэшируются на длительный срок -- если вы закрепляете версию, сопоставьте её с релизом npm `@thunderphone/widget`, для которого ведёте разработку.
</Tip>

---

## Базовое использование

```html
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
</head>
<body>

  <div id="thunderphone"></div>

  <script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
  <script>
    ThunderPhone.mount({
      element: '#thunderphone',
      publishableKey: 'pk_live_your_publishable_key',
    })
  </script>

</body>
</html>
```

Виджет монтируется в целевой элемент и отображается как **панель с фиксированным положением в углу области просмотра** (по умолчанию `bottom-right`, управляется параметром `position`). Элемент монтирования — только корневой элемент React; его расположение на странице не влияет на место отображения виджета.

---

## Параметры монтирования

`ThunderPhone.mount()` принимает те же параметры, что и компонент React, а также свойство `element`:

| Параметр | Тип | Обязательный | По умолчанию | Описание |
|--------|------|----------|---------|-------------|
| `element` | `string \| HTMLElement` | Да | -- | CSS-селектор (например, `'#thunderphone'`) или ссылка на DOM-элемент. |
| `publishableKey` | `string` | Да | -- | Публикуемый ключ API (`pk_live_...`). Агент автоматически определяется по конфигурации виджета ключа. |
| `theme` | `'light' \| 'dark'` | Нет | `'light'` | Цветовая схема. |
| `primaryColor` | `string` | Нет | `'#000000'` (light) / `'#ffffff'` (dark) | Строка CSS-цвета, используемая в качестве акцентного цвета. |
| `title` | `string` | Нет | `'Voice assistant'` | Текст, отображаемый в панели виджета. |
| `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | Нет | `'bottom-right'` | Фиксированное положение в области просмотра. |
| `apiBase` | `string` | Нет | `'https://api.thunderphone.com/v1'` | Переопределение базового URL API. |
| `language` | `string` | Нет | -- | Переопределение языка для сеанса — код языка или локаль, например `en`, `es` или `fr-FR`. Если не задано, используется настроенный язык агента. Требует `language` в [переопределениях браузера](/ru/guides/embed-web-widget) ключа. |
| `voice` | `string` | Нет | -- | Переопределение голоса для сеанса — имя голоса, например `maria`. Если не задано, используется настроенный голос агента. Требует `voice` в [переопределениях браузера](/ru/guides/embed-web-widget) ключа. |
| `context` | `string` | Нет | -- | Фактический контекст страницы или сайта для сеанса, передаваемый агенту. На стороне сервера обрезается до 12 000 символов. Требует `context` в [переопределениях браузера](/ru/guides/embed-web-widget) ключа. |
| `onConnect` | `() => void` | Нет | -- | Вызывается при подключении голосового сеанса. |
| `onDisconnect` | `() => void` | Нет | -- | Вызывается при завершении сеанса. |
| `onError` | `(error) => void` | Нет | -- | Вызывается при ошибках. Объект ошибки содержит поля `error` (код) и `message`. |
| `ringtone` | `boolean \| string` | Нет | `false` | Воспроизводит рингтон во время подключения. `true` — для рингтона по умолчанию, либо строка URL для пользовательского аудио. |

---

## Очистка

`ThunderPhone.mount()` возвращает экземпляр виджета с методами очистки:

```html
<script>
  var widget = ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
  })

  // Later, when you want to remove the widget:
  widget.unmount()

  // Or equivalently:
  widget.destroy()
</script>
```

`unmount()` и `destroy()` выполняют одно и то же — отключают активный голосовой сеанс и удаляют виджет из DOM. Используйте тот вариант, который лучше читается в вашем коде.

<Note>
  Всегда очищайте виджет при переходе со страницы в одностраничных приложениях или при удалении содержащего элемента. Это гарантирует корректное отключение активных голосовых сеансов.
</Note>

---

## Примеры

### Тёмная тема с пользовательским цветом

```html
<div id="thunderphone"></div>

<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    theme: 'dark',
    primaryColor: '#8b5cf6',
    title: 'Talk to our AI',
  })
</script>
```

### Пользовательское положение

```html
<div id="thunderphone"></div>

<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    position: 'bottom-left',
  })
</script>
```

### С обработчиками событий

```html
<div id="thunderphone"></div>

<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    onConnect: function () {
      console.log('Call started')
    },
    onDisconnect: function () {
      console.log('Call ended')
    },
    onError: function (error) {
      console.error('Widget error:', error.error, error.message)
    },
  })
</script>
```

### С рингтоном

```html
<div id="thunderphone"></div>

<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    ringtone: true, // or a custom URL: 'https://example.com/ringtone.mp3'
  })
</script>
```

### Использование ссылки на DOM-элемент

```html
<div id="thunderphone"></div>

<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  var container = document.getElementById('thunderphone')

  ThunderPhone.mount({
    element: container,
    publishableKey: 'pk_live_your_publishable_key',
  })
</script>
```

### Интеграция с WordPress / CMS

Добавьте это в блок пользовательского HTML или в футер вашей темы:

```html
<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>

<div id="thunderphone-widget"></div>
<script>
  ThunderPhone.mount({
    element: '#thunderphone-widget',
    publishableKey: 'pk_live_your_publishable_key',
  })
</script>
```

<Tip>
  `<div>` можно разместить где угодно — это только точка монтирования. Сам виджет отображается как фиксированное наложение в углу области просмотра (задаётся параметром `position`) и не влияет на расположение окружающего содержимого.
</Tip>

---

## Устранение неполадок

<AccordionGroup>
  <Accordion title="Виджет не отображается">
    Убедитесь, что загружены оба файла: CSS и JS. Проверьте консоль браузера на наличие сетевых ошибок. Убедитесь, что целевой элемент существует в DOM до вызова `ThunderPhone.mount()`.
  </Accordion>

  <Accordion title="ThunderPhone не определён">
    Скрипт ещё не загрузился. Убедитесь, что тег `<script>` для `widget.js` расположен перед вызовом монтирования, или оберните вызов монтирования в обработчик `DOMContentLoaded`.
  </Accordion>

  <Accordion title="Ошибка: домен не разрешён">
    Добавьте свой домен в список разрешённых доменов виджета на странице **Веб-виджеты** в [app.thunderphone.com](https://app.thunderphone.com). `localhost` не разрешён автоматически в продакшене; добавьте его в список для локального тестирования.
  </Accordion>
</AccordionGroup>

---

## Следующие шаги

<CardGroup cols={2}>
  <Card title="Стилизация" icon="palette" href="/ru/widget/styling">
    Настройте внешний вид виджета с помощью пользовательских свойств CSS.
  </Card>
  <Card title="Компонент React" icon="react" href="/ru/widget/react">
    Используете React? Интеграция компонента проще.
  </Card>
</CardGroup>
