---
title: "CDN / 脚本标签"
description: "无需打包工具，即可将 ThunderPhone 语音小部件添加到任何网站"
---

CDN 构建在内部捆绑了 React，因此您可以在静态网站、WordPress、Webflow，或任何可以添加 HTML 的页面上使用该小组件。无需 npm、打包工具或框架。

## CDN URL

<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>
  `latest` URL 是控制台嵌入代码生成器生成的 URL。它们会缓存 5 分钟，并自动采用新的稳定版本。固定的 `/widget/vX.Y.Z/` 路径不可变且长期缓存——如果您固定版本，请使其与您开发所使用的 `@thunderphone/widget` npm 发布版本保持一致。
</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'`（浅色）/ `'#ffffff'`（深色） | 用作强调色的 CSS 颜色字符串。 |
| `title` | `string` | 否 | `'Voice assistant'` | 显示在小组件栏中的文本。 |
| `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | 否 | `'bottom-right'` | 固定视口位置。 |
| `apiBase` | `string` | 否 | `'https://api.thunderphone.com/v1'` | API 基础 URL 覆盖值。 |
| `language` | `string` | 否 | -- | 每个会话的语言覆盖值——语言代码或区域设置，例如 `en`、`es` 或 `fr-FR`。未设置时，将使用智能体配置的语言。要求在该密钥的[浏览器覆盖设置](/zh/guides/embed-web-widget)下启用 `language`。 |
| `voice` | `string` | 否 | -- | 每个会话的语音覆盖值——语音名称，例如 `maria`。未设置时，将使用智能体配置的语音。要求在该密钥的[浏览器覆盖设置](/zh/guides/embed-web-widget)下启用 `voice`。 |
| `context` | `string` | 否 | -- | 每个会话传递给智能体的、与页面或站点相关的事实性上下文。服务器端会截断至 12,000 个字符。要求在该密钥的[浏览器覆盖设置](/zh/guides/embed-web-widget)下启用 `context`。 |
| `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 文件均已加载。检查浏览器控制台中是否存在网络错误。请确认在调用 `ThunderPhone.mount()` 前，目标元素已存在于 DOM 中。
  </Accordion>

  <Accordion title="ThunderPhone 未定义">
    脚本尚未加载。请确保 `widget.js` 的 `<script>` 标签位于挂载调用之前，或者将挂载调用包装在 `DOMContentLoaded` 监听器中。
  </Accordion>

  <Accordion title="不允许的域名错误">
    请在 [app.thunderphone.com](https://app.thunderphone.com) 的 **Web 小组件** 页面中，将您的域名添加到该小组件的允许域名列表。生产环境不会自动允许 `localhost`；请将其添加到列表中以进行本地测试。
  </Accordion>
</AccordionGroup>

---

## 后续步骤

<CardGroup cols={2}>
  <Card title="样式" icon="palette" href="/zh/widget/styling">
    使用 CSS 自定义属性自定义小组件外观。
  </Card>
  <Card title="React 组件" icon="react" href="/zh/widget/react">
    使用 React？组件集成更加简单。
  </Card>
</CardGroup>
