@vivix-ai/ivi-frontend-sdk 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +561 -0
- package/dist/index.cjs +3515 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +549 -0
- package/dist/index.d.ts +549 -0
- package/dist/index.js +3505 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
# IVI Frontend SDK
|
|
2
|
+
|
|
3
|
+
用于前端应用集成 IVI 实时能力的 TypeScript SDK,包含:
|
|
4
|
+
|
|
5
|
+
- 基于 `@vivix/ivi-sdk-ts` 的 runtime 协调层
|
|
6
|
+
- 面向舞台渲染的 React 组件与 hooks
|
|
7
|
+
|
|
8
|
+
## 安装依赖
|
|
9
|
+
|
|
10
|
+
当前包尚未发布到 npm,通过 **GitLab 仓库地址**直接安装:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# npm
|
|
14
|
+
npm install git+https://gitlab.vivix.work/vinf-2.0/ivi-sdk/ivi-frontend-sdk.git#main
|
|
15
|
+
|
|
16
|
+
# pnpm
|
|
17
|
+
pnpm add git+https://gitlab.vivix.work/vinf-2.0/ivi-sdk/ivi-frontend-sdk.git#main
|
|
18
|
+
|
|
19
|
+
# yarn
|
|
20
|
+
yarn add git+https://gitlab.vivix.work/vinf-2.0/ivi-sdk/ivi-frontend-sdk.git#main
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> **推荐使用 `#main` 分支。** `dev` 分支可能引入尚未稳定的新特性,不保证向后兼容;`main` 分支仅包含经过验证的变更。如需锁定到更精确的版本,可替换为具体 tag 或 commit hash。
|
|
24
|
+
|
|
25
|
+
安装后 `package.json` 中会出现类似条目:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@vivix/ivi-frontend-sdk": "git+https://gitlab.vivix.work/vinf-2.0/ivi-sdk/ivi-frontend-sdk.git#main"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**更新到最新版本**:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# npm(Git 依赖建议显式指定分支、tag 或 commit)
|
|
39
|
+
npm install git+https://gitlab.vivix.work/vinf-2.0/ivi-sdk/ivi-frontend-sdk.git#main
|
|
40
|
+
|
|
41
|
+
# pnpm
|
|
42
|
+
pnpm add git+https://gitlab.vivix.work/vinf-2.0/ivi-sdk/ivi-frontend-sdk.git#main
|
|
43
|
+
|
|
44
|
+
# 若 lockfile 固定了旧 commit,可重新安装或删除 lock 中该依赖条目后 install
|
|
45
|
+
npm install
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### `dist` 与 Git 直装
|
|
49
|
+
|
|
50
|
+
`package.json` 的 `main` / `module` / `types` / `exports` 均指向 `dist/`,且 npm 包通过 `"files": ["dist"]` 发布。从 **Git 引用安装**(如 `git+https://…#dev`)时,npm 只会打包 `files` 所列内容;因此 **`dist/` 已纳入版本库**,克隆默认分支即可得到可运行、可解析类型的产物。
|
|
51
|
+
|
|
52
|
+
**协作约定**:修改 `src/` 后必须在本地执行 `npm run build`(或 `pnpm run build`),并将**与本次源码变更对应的一次完整 `dist/` 输出**一并提交:可与功能改动放在**同一提交**,或在必要时单独提交,提交信息示例:`chore: 重新构建 dist`。禁止只改源码而不更新 `dist/`,避免仓库内源码与产物不一致。
|
|
53
|
+
|
|
54
|
+
### Peer Dependencies
|
|
55
|
+
|
|
56
|
+
React 为可选 peer dependency,仅使用 React 组件时需要:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
61
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
如果只使用 runtime 能力、不接入 React 组件与 hooks,可忽略本文中的 React 用法部分。
|
|
66
|
+
|
|
67
|
+
## 包导出与导入方式
|
|
68
|
+
|
|
69
|
+
当前仅支持单一主入口:
|
|
70
|
+
|
|
71
|
+
| 入口 | 路径 | 说明 |
|
|
72
|
+
|------|------|------|
|
|
73
|
+
| 主入口 | `@vivix/ivi-frontend-sdk` | 导出 runtime 能力、`IviFrontendSdk`、以及当前公开的 React API:`IVIStageView`、`IVITrackSlot`、`useManagedIviRuntime`、`useIviStageView` |
|
|
74
|
+
|
|
75
|
+
**导入示例**:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// 底层 client 由 @vivix/ivi-sdk-ts 提供
|
|
79
|
+
import { IviClient } from "@vivix/ivi-sdk-ts";
|
|
80
|
+
|
|
81
|
+
// 本包主入口
|
|
82
|
+
import {
|
|
83
|
+
IviRuntimeCoordinator,
|
|
84
|
+
IviRuntimeDispatcher,
|
|
85
|
+
IviFrontendSdk,
|
|
86
|
+
IVIStageView,
|
|
87
|
+
IVITrackSlot,
|
|
88
|
+
useManagedIviRuntime,
|
|
89
|
+
useIviStageView
|
|
90
|
+
} from "@vivix/ivi-frontend-sdk";
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
> 当前包没有 `@vivix/ivi-frontend-sdk/core` / `@vivix/ivi-frontend-sdk/react` 子入口;底层 WebSocket client、事件解析与协议类型请直接从 `@vivix/ivi-sdk-ts` 导入。
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 快速开始
|
|
98
|
+
|
|
99
|
+
### 1)创建 client + runtime
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { IviClient } from "@vivix/ivi-sdk-ts";
|
|
103
|
+
import { IviRuntimeCoordinator } from "@vivix/ivi-frontend-sdk";
|
|
104
|
+
|
|
105
|
+
const client = new IviClient({
|
|
106
|
+
url: "wss://your-domain/v1/realtime",
|
|
107
|
+
sessionId: "sess_xxx",
|
|
108
|
+
onParseError: (error, raw) => {
|
|
109
|
+
console.error("parse error", error, raw);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const runtime = new IviRuntimeCoordinator(client, {
|
|
114
|
+
syncStageOnSessionCreated: true, // 默认 true,session 建立后自动同步 stage
|
|
115
|
+
onSessionEnded: (event) => {
|
|
116
|
+
console.log("session ended", event);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
await runtime.start();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**使用自定义 WebSocket 运行时**(Node.js / 自定义环境):
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import WebSocket from "ws"; // Node.js 环境
|
|
127
|
+
import { IviClient } from "@vivix/ivi-sdk-ts";
|
|
128
|
+
import { IviRuntimeCoordinator } from "@vivix/ivi-frontend-sdk";
|
|
129
|
+
|
|
130
|
+
const client = new IviClient({
|
|
131
|
+
url: "wss://your-domain/v1/realtime",
|
|
132
|
+
sessionId: "sess_xxx",
|
|
133
|
+
socketFactory: (url, protocols) => new WebSocket(url, protocols),
|
|
134
|
+
onParseError: (error, raw) => {
|
|
135
|
+
console.error("parse error", error, raw);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const runtime = new IviRuntimeCoordinator(client);
|
|
140
|
+
await runtime.start();
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`IviClientConfig` 关键配置:
|
|
144
|
+
|
|
145
|
+
| 参数 | 类型 | 说明 |
|
|
146
|
+
|------|------|------|
|
|
147
|
+
| `protocols` | `string \| string[]` | WebSocket 子协议 |
|
|
148
|
+
| `sessionId` | `string` | 会话 ID,连接时自动追加到 URL query `session_id=<id>` |
|
|
149
|
+
| `socketFactory` | `(url, protocols?) => WebSocket` | 自定义 Socket 工厂,用于替换默认 WebSocket 构造 |
|
|
150
|
+
| `onParseError` | `(error, rawMessage) => void` | 事件解析失败回调 |
|
|
151
|
+
| `onLog` | `(entry) => void` | `IviClient` 链路日志回调,包含发送、WebSocket 状态和重连链路 |
|
|
152
|
+
| `reconnect` | `IviClientReconnectOptions` | 自动重连配置;默认启用,指数退避 |
|
|
153
|
+
| `sendWaitTimeoutMs` | `number` | 发送时等待连接恢复的超时,默认 `30000` ms,`<=0` 表示无限等待 |
|
|
154
|
+
|
|
155
|
+
### 2)订阅状态与事件
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const unlistenState = runtime.onStateChange((state) => {
|
|
159
|
+
console.log(state.status, state.stage, state.tracks.size, state.sources.size);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const unlistenEvent = runtime.onEvent((event, state) => {
|
|
163
|
+
console.log("event:", event.type, "runtime:", state.status);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// 业务结束时
|
|
167
|
+
unlistenState();
|
|
168
|
+
unlistenEvent();
|
|
169
|
+
runtime.stop();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 3)发送用户文本并触发模型回复
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
const result = await runtime.sendUserTextAndTriggerStreamResponse({
|
|
176
|
+
streamId: "stream_001",
|
|
177
|
+
text: "你好",
|
|
178
|
+
callbacks: {
|
|
179
|
+
onConversationItemAdded: (event) => console.log("item added"),
|
|
180
|
+
onConversationItemDone: (event) => console.log("item done"),
|
|
181
|
+
onConversationItemReady: ({ itemId }) => console.log("item ready:", itemId),
|
|
182
|
+
onStreamResponseCreated: (event) => console.log("response created")
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
console.log("完成:", result.itemId, result.streamId);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 4)上报 source 播放完成
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
runtime.sendSessionSourcePlaybackCompleted("source_001", "track_001");
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 5)runtime 状态字段
|
|
195
|
+
|
|
196
|
+
`IviRuntimeState` 包含:
|
|
197
|
+
|
|
198
|
+
| 字段 | 类型 | 说明 |
|
|
199
|
+
|------|------|------|
|
|
200
|
+
| `status` | `"idle" \| "connecting" \| "syncing" \| "running" \| "stopped"` | 生命周期状态 |
|
|
201
|
+
| `session` | `IviRealtimeSession \| null` | 当前会话 |
|
|
202
|
+
| `stage` | `IviStage \| null` | 当前 stage(slot → track 映射) |
|
|
203
|
+
| `tracks` | `Map<string, IviTrack>` | 所有 track |
|
|
204
|
+
| `sources` | `Map<string, IviRuntimeSource>` | 所有 source(含 `status: created \| ready \| failed`) |
|
|
205
|
+
| `conversationItems` | `Map<string, IviRuntimeConversationItem>` | 对话条目映射 |
|
|
206
|
+
| `conversations` | `IviRuntimeConversationItem[]` | 对话有序列表 |
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## React 接入
|
|
211
|
+
|
|
212
|
+
### 方式 A:传入外部 runtime
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
import { IviClient } from "@vivix/ivi-sdk-ts";
|
|
216
|
+
import { IviRuntimeCoordinator, IVIStageView, IVITrackSlot, useIviStageView } from "@vivix/ivi-frontend-sdk";
|
|
217
|
+
|
|
218
|
+
// 初始化 runtime(参考上方 Core 用法)
|
|
219
|
+
const client = new IviClient({ url: "wss://your-domain/v1/realtime", sessionId: "sess_xxx" });
|
|
220
|
+
const runtime = new IviRuntimeCoordinator(client);
|
|
221
|
+
await runtime.start();
|
|
222
|
+
|
|
223
|
+
function StageDebug() {
|
|
224
|
+
const { slotBindings } = useIviStageView();
|
|
225
|
+
return <pre>{slotBindings.map((x) => `${x.slot} → ${x.trackId} → ${x.sourceId ?? "null"}`).join("\n")}</pre>;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function StagePage() {
|
|
229
|
+
return (
|
|
230
|
+
<IVIStageView runtime={runtime}>
|
|
231
|
+
{({ slotBindings }) => (
|
|
232
|
+
<div>
|
|
233
|
+
{slotBindings.map((binding) => (
|
|
234
|
+
<IVITrackSlot key={binding.slot} slot={binding.slot} />
|
|
235
|
+
))}
|
|
236
|
+
<StageDebug />
|
|
237
|
+
</div>
|
|
238
|
+
)}
|
|
239
|
+
</IVIStageView>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### 方式 B:使用 `useManagedIviRuntime` 管理 runtime
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
import { IVIStageView, IVITrackSlot, useManagedIviRuntime } from "@vivix/ivi-frontend-sdk";
|
|
248
|
+
|
|
249
|
+
export function AutoStagePage({ sessionId }: { sessionId: string }) {
|
|
250
|
+
const runtime = useManagedIviRuntime({
|
|
251
|
+
clientConfig: {
|
|
252
|
+
url: "https://your-domain.com/v1/realtime",
|
|
253
|
+
sessionId
|
|
254
|
+
},
|
|
255
|
+
onLog: (entry) => {
|
|
256
|
+
// 统一收集 client / runtime / TRTC 的 IVI 链路日志
|
|
257
|
+
console.log(entry.tag, entry.message, entry.data);
|
|
258
|
+
},
|
|
259
|
+
onRuntimeInitError: (err) => console.error("runtime init failed", err)
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<IVIStageView runtime={runtime} onRuntimeEvent={(event) => console.log("event:", event.type)}>
|
|
264
|
+
<IVITrackSlot slot="main" showVolumeControl showSubtitle />
|
|
265
|
+
</IVIStageView>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
`useManagedIviRuntime` 的地址规则:
|
|
271
|
+
|
|
272
|
+
1. 使用 `clientConfig.url`
|
|
273
|
+
2. 若 `clientConfig.url` 为 `https://` / `http://`,内部会自动确保为 `wss://` / `ws://`
|
|
274
|
+
3. 若 `clientConfig.sessionId` 为空,则不创建 runtime
|
|
275
|
+
4. 若 `clientConfig.url` 为空,会通过 `onRuntimeInitError` 抛出初始化错误;若未提供回调,则直接抛错
|
|
276
|
+
|
|
277
|
+
连接时自动追加 query:`?session_id=<sessionId>`。
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## React Hooks
|
|
282
|
+
|
|
283
|
+
### `useManagedIviRuntime`
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
function useManagedIviRuntime(config: IviManagedRuntimeConfig): IviRuntimeCoordinator | null;
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
负责根据 `clientConfig` 创建并管理一个 `IviRuntimeCoordinator`。该 hook 会处理:
|
|
290
|
+
|
|
291
|
+
- `IviClient` / `IviRuntimeCoordinator` 的创建
|
|
292
|
+
- `autoStart` 为 `true` 时自动调用 `runtime.start()`
|
|
293
|
+
- 卸载时自动 `runtime.stop()`
|
|
294
|
+
- 初始化错误通过 `onRuntimeInitError` 上抛
|
|
295
|
+
- 通过 `onLog` 统一收集 `IviClient`、runtime 状态变更、TRTC 管理器等 IVI 链路日志
|
|
296
|
+
|
|
297
|
+
`IviManagedRuntimeConfig` 关键字段:
|
|
298
|
+
|
|
299
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
300
|
+
|------|------|--------|------|
|
|
301
|
+
| `clientConfig` | `IviClientConfig` | — | `IviClient` 配置;其中 `url` 用于建立连接,`sessionId` 用于决定是否创建 runtime |
|
|
302
|
+
| `autoStart` | `boolean` | `true` | 是否自动启动 runtime |
|
|
303
|
+
| `runtimeConfig` | `IviRuntimeCoordinatorConfig` | — | 透传给 `IviRuntimeCoordinator` |
|
|
304
|
+
| `onRuntimeInitError` | `(error) => void` | — | 初始化或启动失败回调 |
|
|
305
|
+
| `onLog` | `(entry) => void` | — | 统一日志回调,覆盖 `[IVI-SEND]`、`[IVI-WS]`、`[IVI-RECONNECT]`、`[IVI-EVT]`、`[IVI-STATE]`、`[IVI-TRTC]` |
|
|
306
|
+
|
|
307
|
+
`onLog` 的统一入参格式:
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
interface IviManagedRuntimeLogEntry {
|
|
311
|
+
level: "info" | "warn" | "error";
|
|
312
|
+
source: "client" | "runtime";
|
|
313
|
+
tag: string; // 例如 "[IVI-WS]"、"[IVI-STATE]"
|
|
314
|
+
message: string; // 带 tag 的可读日志
|
|
315
|
+
args: unknown[]; // 接近原 console 调用的参数
|
|
316
|
+
data?: unknown; // 结构化数据
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### `useIviStageView`
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
function useIviStageView(): IviStageViewContextValue;
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
读取最近 `IVIStageView` 提供的 Context,必须在 `IVIStageView` 子树内使用。
|
|
327
|
+
|
|
328
|
+
返回值 `IviStageViewContextValue`:
|
|
329
|
+
|
|
330
|
+
| 字段 | 类型 | 说明 |
|
|
331
|
+
|------|------|------|
|
|
332
|
+
| `runtime` | `IviRuntimeCoordinator \| null` | 当前 runtime 实例 |
|
|
333
|
+
| `state` | `IviRuntimeState` | 实时状态快照 |
|
|
334
|
+
| `slotTrackMap` | `Map<string, string>` | slot → trackId 映射 |
|
|
335
|
+
| `slotBindings` | `IviStageSlotBinding[]` | 完整绑定信息数组 |
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
function MyOverlay() {
|
|
339
|
+
const { state, slotBindings } = useIviStageView();
|
|
340
|
+
return <div>{state.conversations.length} 条对话 | {slotBindings.length} 个绑定</div>;
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
以下 hooks 为 `IVITrackSlot` 内部实现,当前不属于公开导出 API。
|
|
345
|
+
|
|
346
|
+
### `useVolumeMemory`
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
function useVolumeMemory(mediaType: IviMediaVolumeType | null): [number, (v: number) => void];
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
按媒体类型(`"trtc" | "video" | "hls"`)在 `localStorage` 中持久化音量(0–100)。`mediaType` 为 `null` 时不持久化。
|
|
353
|
+
|
|
354
|
+
### `useApplyVolumeToSlot`
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
function useApplyVolumeToSlot(
|
|
358
|
+
containerRef: RefObject<HTMLElement | null>,
|
|
359
|
+
volume: number,
|
|
360
|
+
enabled: boolean,
|
|
361
|
+
activeSourceId?: string | null
|
|
362
|
+
): void;
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
将音量同步应用到容器内带 `data-ivi-slot-role="active"` 属性的 `<video>` / `<audio>` 元素,通过 `MutationObserver` 监听异步插入的媒体(如 TRTC 远端流)。
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## React 组件
|
|
370
|
+
|
|
371
|
+
当前公开导出的 React 组件只有 `IVIStageView` 与 `IVITrackSlot`;当前公开导出的 React hooks 只有 `useManagedIviRuntime` 与 `useIviStageView`。
|
|
372
|
+
|
|
373
|
+
### `IVIStageView`
|
|
374
|
+
|
|
375
|
+
顶层舞台容器,负责消费 `runtime` 并通过 Context 注入状态。
|
|
376
|
+
|
|
377
|
+
| Prop | 类型 | 默认值 | 说明 |
|
|
378
|
+
|------|------|--------|------|
|
|
379
|
+
| `children` | `ReactNode \| (ctx) => ReactNode` | — | 子节点或 render-prop |
|
|
380
|
+
| `runtime` | `IviRuntimeCoordinator \| null` | — | 要消费的 runtime,由业务方或 `useManagedIviRuntime` 提供 |
|
|
381
|
+
| `onBindingsChange` | `(bindings) => void` | — | slot 绑定变化回调 |
|
|
382
|
+
| `onRuntimeEvent` | `(event) => void` | — | runtime 事件回调 |
|
|
383
|
+
| `className` / `style` | — | — | 容器样式 |
|
|
384
|
+
|
|
385
|
+
### `IVITrackSlot`
|
|
386
|
+
|
|
387
|
+
按 slot 或 trackId 绑定,自动根据 source 类型渲染对应媒体播放器。
|
|
388
|
+
|
|
389
|
+
| Prop | 类型 | 默认值 | 说明 |
|
|
390
|
+
|------|------|--------|------|
|
|
391
|
+
| `slot` | `string` | — | 通过 slot 名称绑定(与 stage composition 对应) |
|
|
392
|
+
| `trackId` | `string` | — | 直接绑定 trackId |
|
|
393
|
+
| `emptyFallback` | `ReactNode` | — | 无 source 时的兜底内容 |
|
|
394
|
+
| `renderTrtc` | `(ctx: IviTrackSlotRenderContext) => ReactNode` | — | 自定义 TRTC 渲染 |
|
|
395
|
+
| `renderMedia` | `(ctx: IviTrackSlotRenderContext) => ReactNode` | — | 自定义非 TRTC 媒体渲染 |
|
|
396
|
+
| `fitStrategy` | `"contain" \| "cover" \| "auto"` | — | 媒体适配策略 |
|
|
397
|
+
| `adaptToSourceSize` | `boolean` | — | 是否根据 source 尺寸自适应 |
|
|
398
|
+
| `background` | `IviTrackSlotBackground` | `"black"` | 媒体空白区域(letterbox/pillarbox)背景模式 |
|
|
399
|
+
| `showVolumeControl` | `boolean` | — | 是否显示音量控制浮层 |
|
|
400
|
+
| `volumeControlProps` | — | — | 音量控制自定义配置 |
|
|
401
|
+
| `showSubtitle` | `boolean` | — | 是否显示字幕浮层 |
|
|
402
|
+
| `subtitleProps` | — | — | 字幕自定义配置 |
|
|
403
|
+
| `trtcPlayerProps` | — | — | TRTC 播放器自定义配置 |
|
|
404
|
+
| `videoProps` / `imageProps` | — | — | 透传给原生 `<video>` / `<img>` |
|
|
405
|
+
| `className` / `style` | — | — | 容器样式 |
|
|
406
|
+
|
|
407
|
+
`IviTrackSlotBackground` 支持的值:`"black"` | `"white"` | `"transparent"` | `{ color: string }` | `"blur"` | `{ blur: "live" | "static" }`
|
|
408
|
+
|
|
409
|
+
`IviTrackSlotRenderContext`:
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
interface IviTrackSlotRenderContext {
|
|
413
|
+
slot: string;
|
|
414
|
+
track: IviTrack;
|
|
415
|
+
source: IviRuntimeSource;
|
|
416
|
+
isPreloading?: boolean;
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
以下组件为 `IVITrackSlot` 依赖的内部实现,当前不属于公开导出 API。
|
|
421
|
+
|
|
422
|
+
### `IVITrtcPlayer`
|
|
423
|
+
|
|
424
|
+
TRTC 远端流播放器,自动以 audience 身份入会并订阅远端流。
|
|
425
|
+
|
|
426
|
+
| Prop | 类型 | 说明 |
|
|
427
|
+
|------|------|------|
|
|
428
|
+
| `trtc` | TRTC 实例 | TRTC SDK 实例 |
|
|
429
|
+
| `sourceId` | `string` | source ID |
|
|
430
|
+
| `runtime` | `IviRuntimeCoordinator` | runtime 实例 |
|
|
431
|
+
| `loadingFallback` | `ReactNode` | 加载中兜底 |
|
|
432
|
+
| `errorFallback` | `ReactNode` | 错误兜底 |
|
|
433
|
+
| `muted` | `boolean` | 是否静音 |
|
|
434
|
+
| `className` / `style` | — | 容器样式 |
|
|
435
|
+
|
|
436
|
+
### `IVIHlsVideo`
|
|
437
|
+
|
|
438
|
+
HLS 流播放器(基于 `hls.js`),用于 `.m3u8` 地址播放。辅助函数 `isM3u8Url(url)` 可判断 URL 是否为 m3u8。
|
|
439
|
+
|
|
440
|
+
| Prop | 类型 | 说明 |
|
|
441
|
+
|------|------|------|
|
|
442
|
+
| `url` | `string` | HLS 播放地址 |
|
|
443
|
+
| `videoProps` | — | 透传给 `<video>` |
|
|
444
|
+
| `style` | — | 样式 |
|
|
445
|
+
| `aggressivePreload` | `boolean` | 激进预加载模式 |
|
|
446
|
+
| `paused` | `boolean` | 是否暂停 |
|
|
447
|
+
|
|
448
|
+
> 隐藏原生控件(包括进度条):SDK 未提供单独隐藏进度条的开关,HTML5 `<video>` 也不支持只隐藏 timeline。可通过 `videoProps.controls = false` 关闭整套原生控件,适用于 `IVIHlsVideo` 与 `IVITrackSlot`:
|
|
449
|
+
>
|
|
450
|
+
> ```tsx
|
|
451
|
+
> <IVIHlsVideo url={url} videoProps={{ controls: false }} />
|
|
452
|
+
> <IVITrackSlot slot="main" videoProps={{ controls: false }} />
|
|
453
|
+
> ```
|
|
454
|
+
>
|
|
455
|
+
> 若需“保留播放/音量,仅隐藏进度条”,请在上层自行渲染自定义控制栏。
|
|
456
|
+
|
|
457
|
+
### `IVIVolumeControl`
|
|
458
|
+
|
|
459
|
+
音量控制浮层组件。
|
|
460
|
+
|
|
461
|
+
| Prop | 类型 | 说明 |
|
|
462
|
+
|------|------|------|
|
|
463
|
+
| `volume` | `number` | 当前音量 0–100 |
|
|
464
|
+
| `onVolumeChange` | `(volume: number) => void` | 音量变化回调 |
|
|
465
|
+
| `renderSpeakerIcon` | `(volume: number) => ReactNode` | 自定义喇叭图标 |
|
|
466
|
+
| `colors` | `IVIVolumeControlColors` | 颜色配置 |
|
|
467
|
+
| `className` / `style` | — | 样式 |
|
|
468
|
+
|
|
469
|
+
### `IVISubtitleOverlay`
|
|
470
|
+
|
|
471
|
+
字幕浮层组件,展示对话条目流式字幕。
|
|
472
|
+
|
|
473
|
+
| Prop | 类型 | 默认值 | 说明 |
|
|
474
|
+
|------|------|--------|------|
|
|
475
|
+
| `conversations` | `IviRuntimeConversationItem[]` | — | 会话条目列表 |
|
|
476
|
+
| `maxVisible` | `number` | `2` | 同时可见的最大条目数 |
|
|
477
|
+
| `dismissAfterMs` | `number` | `5000` | 条目完成后自动消失毫秒数 |
|
|
478
|
+
| `subtitleStyle` | `IVISubtitleOverlayStyle` | — | 样式配置 |
|
|
479
|
+
| `className` / `style` | — | — | 样式 |
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## 主动发送事件(IviClient 便捷方法)
|
|
484
|
+
|
|
485
|
+
`IviClient` 内置以下发送方法(自动补齐 `event_id`):
|
|
486
|
+
|
|
487
|
+
| 方法 | 说明 |
|
|
488
|
+
|------|------|
|
|
489
|
+
| `sendSessionStageGet()` | 请求当前 stage |
|
|
490
|
+
| `sendSessionTracksList()` | 请求 track 列表 |
|
|
491
|
+
| `sendSessionSourcesList()` | 请求 source 列表 |
|
|
492
|
+
| `sendSessionStreamsList()` | 请求 stream 列表 |
|
|
493
|
+
| `sendSessionSourcePlaybackCompleted(sourceId, trackId?)` | 上报 source 播放完成 |
|
|
494
|
+
| `sendConversationList()` | 请求对话列表 |
|
|
495
|
+
| `sendConversationItemCreate(item)` | 创建对话条目 |
|
|
496
|
+
| `sendConversationUserText(text, itemId?)` | 发送用户文本,返回 `Promise<string>`,resolve 为最终 itemId |
|
|
497
|
+
| `sendResponseCreate(streamId?, response?)` | 触发模型回复;`streamId` 可选,省略时使用 session 级配置 |
|
|
498
|
+
| `sendResponseCreateByItemId(itemId, streamId?, response?)` | 基于条目 ID 触发模型回复 |
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
## 已解析事件范围
|
|
503
|
+
|
|
504
|
+
`parseIviEvent` 已支持并有测试覆盖:
|
|
505
|
+
|
|
506
|
+
- `session.created`、`session.updated`、`session.ended`
|
|
507
|
+
- `session.stream.*`、`session.streams.list.response`
|
|
508
|
+
- `response.*`
|
|
509
|
+
- `response.output_text.*`
|
|
510
|
+
- `response.output_audio_transcript.*`
|
|
511
|
+
- `session.stage.updated`、`session.stage.get.response`
|
|
512
|
+
- `session.track.*`、`session.tracks.list.response`
|
|
513
|
+
- `session.source.*`、`session.sources.list.response`
|
|
514
|
+
- `session.character.*`、`session.characters.list.response`
|
|
515
|
+
- `session.environment.*`、`session.environments.list.response`
|
|
516
|
+
- `session.object.*`、`session.objects.list.response`
|
|
517
|
+
- `conversation.list.response`
|
|
518
|
+
- `conversation.item.added`、`conversation.item.done`
|
|
519
|
+
- `error`
|
|
520
|
+
|
|
521
|
+
未识别类型会降级为 `UnknownIviEvent`,避免解析阶段直接中断。
|
|
522
|
+
|
|
523
|
+
## 语义约束
|
|
524
|
+
|
|
525
|
+
- `session`:一次 IVI 体验的最外层作用域;
|
|
526
|
+
- `stage`:展示编排层,维护 `slot → track`;
|
|
527
|
+
- `track`:展示播控通道,维护 active/next source 引用;
|
|
528
|
+
- `source`:可被 track 播放的媒体资产(含 playback 信息);
|
|
529
|
+
- `stream`:实时推理通道,属于生成层概念,不等价于展示层 track。
|
|
530
|
+
|
|
531
|
+
## 当前已知边界
|
|
532
|
+
|
|
533
|
+
- 底层 `IviClient` 默认启用自动重连,并支持发送时等待连接恢复;但当前不内置应用层心跳;
|
|
534
|
+
- 不内置事件去重缓存;
|
|
535
|
+
- 不提供 Web Component 入口;
|
|
536
|
+
- 对外以 TypeScript/React API 为主。
|
|
537
|
+
|
|
538
|
+
## 本地开发
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
npm install
|
|
542
|
+
npm run typecheck
|
|
543
|
+
npm run build
|
|
544
|
+
npm run test
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### 版本提升与打 tag
|
|
548
|
+
|
|
549
|
+
仓库提供版本提升脚本,执行前要求工作区干净:
|
|
550
|
+
|
|
551
|
+
```bash
|
|
552
|
+
npm run release:version -- 0.2.2
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
脚本会同步更新 `package.json` / `package-lock.json`,提交:
|
|
556
|
+
|
|
557
|
+
```text
|
|
558
|
+
chore: update package version to 0.2.2
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
并创建 tag:`v0.2.2`。
|