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

# CDN / Thẻ Script

> Thêm tiện ích giọng nói ThunderPhone vào bất kỳ trang web nào mà không cần bundler

Bản dựng CDN đóng gói React nội bộ, vì vậy bạn có thể sử dụng widget trên trang web tĩnh, WordPress, Webflow hoặc bất kỳ trang nào mà bạn có thể thêm HTML. Không cần npm, bundler hay framework.

## URL CDN

<CodeGroup>
  ```html Latest (recommended) theme={null}
  <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 theme={null}
  <!-- 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>
  Các URL `latest` là những URL mà trình tạo đoạn mã nhúng của dashboard tạo ra. Chúng được lưu vào bộ nhớ đệm trong 5 phút và tự động nhận các bản phát hành ổn định mới. Các đường dẫn `/widget/vX.Y.Z/` được ghim là bất biến và được lưu vào bộ nhớ đệm dài hạn -- nếu ghim, hãy khớp phiên bản với bản phát hành npm `@thunderphone/widget` mà bạn phát triển dựa trên đó.
</Tip>

***

## Cách sử dụng cơ bản

```html theme={null}
<!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>
```

Widget được gắn vào phần tử đích và hiển thị dưới dạng **thanh có vị trí cố định ở một góc của khung nhìn** (mặc định là `bottom-right`, được kiểm soát bằng tùy chọn `position`). Phần tử gắn chỉ là gốc React -- vị trí của nó trong trang không ảnh hưởng đến vị trí widget xuất hiện.

***

## Tùy chọn gắn

`ThunderPhone.mount()` chấp nhận các tùy chọn giống thành phần React, cùng với thuộc tính `element`:

| Tùy chọn         | Loại                                                           | Bắt buộc | Mặc định                               | Mô tả                                                                                                                                                             |
| ---------------- | -------------------------------------------------------------- | -------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `element`        | `string \| HTMLElement`                                        | Có       | --                                     | Bộ chọn CSS (ví dụ: `'#thunderphone'`) hoặc tham chiếu phần tử DOM.                                                                                               |
| `publishableKey` | `string`                                                       | Có       | --                                     | Khóa API công khai (`pk_live_...`). Tác nhân AI được xác định tự động từ cấu hình widget của khóa.                                                                |
| `theme`          | `'light' \| 'dark'`                                            | Không    | `'light'`                              | Bảng màu.                                                                                                                                                         |
| `primaryColor`   | `string`                                                       | Không    | `'#000000'` (sáng) / `'#ffffff'` (tối) | Chuỗi màu CSS dùng làm màu nhấn.                                                                                                                                  |
| `title`          | `string`                                                       | Không    | `'Voice assistant'`                    | Văn bản hiển thị trong thanh widget.                                                                                                                              |
| `position`       | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | Không    | `'bottom-right'`                       | Vị trí cố định trong khung nhìn.                                                                                                                                  |
| `apiBase`        | `string`                                                       | Không    | `'https://api.thunderphone.com/v1'`    | Ghi đè URL cơ sở API.                                                                                                                                             |
| `language`       | `string`                                                       | Không    | --                                     | Ghi đè ngôn ngữ theo phiên -- mã ngôn ngữ hoặc ngôn ngữ khu vực như `en`, `es` hoặc `fr-FR`. Khi không đặt, ngôn ngữ đã cấu hình của tác nhân AI sẽ được áp dụng. |
| `voice`          | `string`                                                       | Không    | --                                     | Ghi đè giọng nói theo phiên -- tên giọng nói như `maria`. Khi không đặt, giọng nói đã cấu hình của tác nhân AI sẽ được áp dụng.                                   |
| `context`        | `string`                                                       | Không    | --                                     | Ngữ cảnh thực tế của trang hoặc trang web theo phiên được truyền cho tác nhân AI. Bị cắt ngắn ở phía máy chủ còn 12.000 ký tự.                                    |
| `onConnect`      | `() => void`                                                   | Không    | --                                     | Được gọi khi phiên giọng nói kết nối.                                                                                                                             |
| `onDisconnect`   | `() => void`                                                   | Không    | --                                     | Được gọi khi phiên kết thúc.                                                                                                                                      |
| `onError`        | `(error) => void`                                              | Không    | --                                     | Được gọi khi có lỗi. Lỗi có các trường `error` (mã) và `message`.                                                                                                 |
| `ringtone`       | `boolean \| string`                                            | Không    | `false`                                | Phát nhạc chuông trong khi kết nối. `true` dùng nhạc chuông mặc định, hoặc chuỗi URL cho âm thanh tùy chỉnh.                                                      |

***

## Dọn dẹp

`ThunderPhone.mount()` trả về một thực thể widget có các phương thức dọn dẹp:

```html theme={null}
<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>
```

Cả `unmount()` và `destroy()` đều thực hiện cùng một việc -- ngắt kết nối mọi phiên thoại đang hoạt động và xóa widget khỏi DOM. Sử dụng cách nào dễ đọc hơn trong mã của bạn.

<Note>
  Luôn dọn dẹp widget khi điều hướng đi trong ứng dụng một trang hoặc khi phần tử chứa bị xóa. Điều này đảm bảo các phiên thoại đang hoạt động được ngắt kết nối đúng cách.
</Note>

***

## Ví dụ

### Giao diện tối với màu tùy chỉnh

```html theme={null}
<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>
```

### Vị trí tùy chỉnh

```html theme={null}
<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>
```

### Với callback sự kiện

```html theme={null}
<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>
```

### Với nhạc chuông

```html theme={null}
<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>
```

### Sử dụng tham chiếu phần tử DOM

```html theme={null}
<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>
```

### Tích hợp WordPress / CMS

Thêm nội dung này vào khối HTML tùy chỉnh hoặc chân trang của giao diện:

```html theme={null}
<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>` có thể đặt ở bất kỳ đâu -- đây chỉ là điểm gắn. Bản thân widget hiển thị dưới dạng lớp phủ cố định ở một góc khung nhìn (được thiết lập bằng tùy chọn `position`) và không nằm trong luồng nội dung xung quanh.
</Tip>

***

## Khắc phục sự cố

<AccordionGroup>
  <Accordion title="Widget không xuất hiện">
    Đảm bảo cả tệp CSS và JS đều đã được tải. Kiểm tra console của trình duyệt để tìm lỗi mạng. Xác minh phần tử đích tồn tại trong DOM trước khi gọi `ThunderPhone.mount()`.
  </Accordion>

  <Accordion title="ThunderPhone chưa được định nghĩa">
    Script chưa được tải. Đảm bảo thẻ `<script>` cho `widget.js` xuất hiện trước lệnh gọi mount, hoặc bọc lệnh gọi mount trong trình lắng nghe `DOMContentLoaded`.
  </Accordion>

  <Accordion title="Lỗi tên miền không được phép">
    Thêm tên miền của bạn vào danh sách tên miền được phép trong phần cài đặt **Nhà phát triển** tại [app.thunderphone.com](https://app.thunderphone.com). Lưu ý rằng `localhost` luôn được phép.
  </Accordion>
</AccordionGroup>

***

## Bước tiếp theo

<CardGroup cols={2}>
  <Card title="Tạo kiểu" icon="palette" href="/vi/widget/styling">
    Tùy chỉnh giao diện widget bằng các thuộc tính tùy chỉnh CSS.
  </Card>

  <Card title="Thành phần React" icon="react" href="/vi/widget/react">
    Bạn đang dùng React? Việc tích hợp thành phần đơn giản hơn.
  </Card>
</CardGroup>
