# xinyao-chat-embed-sdk（聊天窗 iframe 控制层）

第三方网站接入信遥智能客服的最小组件：把独立聊天页（chat-page）包进 `<iframe>`，
控制宽高、移动（拖拽）、显示/隐藏，通过 postMessage 注入 `app_key`/`app_secret`
（密钥不进入 URL），并透传页面事件。**零依赖，纯 DOM**，任意框架 / 原生 HTML 可用。

## 为什么需要它

聊天页运行在信遥自己的域名下（MinIO 上传不跨域），第三方网站只负责"开一个 iframe"。
本 SDK 就是那个"iframe 遥控器"。

## 快速开始

```bash
pnpm install
pnpm build        # 产物在 dist/
```

### 原生 HTML（推荐，Web Component）

```html
<xinyao-chat-embed
  page-url="https://chat.你的域/chat.html"
  app-key="你的app_key"
  app-secret="你的app_secret"
  channel-no="klXPrSp8"
  width="360"
  height="600"
></xinyao-chat-embed>
<script src="./dist/xinyao-chat-embed-sdk.umd.cjs"></script>
```

### JS（任意框架）

```js
import { createChatWidget } from 'xinyao-chat-embed-sdk';

const widget = createChatWidget({
  pageUrl: 'https://chat.你的域/chat.html',
  appKey: '你的app_key',
  appSecret: '你的app_secret',
  channelNo: 'klXPrSp8',
  baseURL: 'https://dev.xinyaoai.com',
  wsURL: 'wss://wss.xinyaoai.com:9641/ws/chat/{u_key}',
  position: { right: 20, bottom: 20 },
  onReady: () => console.log('聊天页就绪'),
  onOpen: ({ sid }) => console.log('会话开启', sid),
  onClose: () => console.log('会话关闭'),
  onError: (e) => console.error(e),
});

widget.setSize(400, 700);
widget.moveTo(40, 40);
widget.hide();
widget.show();
widget.toggle();
widget.destroy();
```

## API

### createChatWidget(options) / new XinyaoChatEmbed(options)

| 选项 | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `pageUrl` | string | 必填 | chat-page 地址（不含密钥参数） |
| `appKey` / `appSecret` | string | - | 应用密钥对，经 postMessage 注入页面 |
| `appId` | string | - | 租户 ID，随请求上送 |
| `channelNo` | string | - | 渠道编号 |
| `baseURL` / `wsURL` | string | - | HTTP 网关 / WS 网关（缺省用信遥测试环境） |
| `container` | HTMLElement \| string | - | 挂载容器；不传则 fixed 悬浮右下角 |
| `width` / `height` | number | 360 / 600 | 初始尺寸 |
| `position` | object | `{right:20,bottom:20}` | fixed 模式初始位置 |
| `draggable` | boolean | true | 是否可拖拽 |
| `dragHandle` | boolean | true | 是否显示顶部拖拽条 |
| `radius` | number | 8 | 圆角 |
| `onReady` / `onOpen` / `onClose` / `onError` | fn | - | 页面事件回调 |

### 实例方法

- `show()` / `hide()` / `toggle()` —— 显隐（隐藏不销毁会话）
- `setSize(w, h)` —— 设置宽高
- `moveTo(left, top)` —— 移动
- `destroy()` —— 销毁

## postMessage 协议（与 chat-page 约定）

| 方向 | type | payload |
| --- | --- | --- |
| 父 → 页面 | `xinyao-chat:config` | 运行配置（含密钥） |
| 页面 → 父 | `xinyao-chat:ready` | - |
| 页面 → 父 | `xinyao-chat:open` | `{ sid }` |
| 页面 → 父 | `xinyao-chat:close` | - |
| 页面 → 父 | `xinyao-chat:error` | `{ message }` |

## 安全说明

- 密钥仅通过 postMessage 注入，**不写入 URL**（不留在浏览器历史）；
- 父窗口只接受 `event.source === iframe.contentWindow` 的消息，防止伪造；
- iframe 未启用 sandbox（聊天页需要 localStorage 存 u_key），接入方可在
  `pageUrl` 指向的网关层自行做域名白名单校验。
