@firetable/project-xiaochun 0.1.17 → 0.1.19
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-CN.md +9 -5
- package/README.md +9 -5
- package/dist/avatar-element.d.ts +3 -1
- package/dist/chunks/{chunk-BJF55B44.js → chunk-RTBOYZAZ.js} +6 -3
- package/dist/chunks/chunk-RTBOYZAZ.js.map +7 -0
- package/dist/chunks/{chunk-4RT7LXB5.js → chunk-SPCT7EQA.js} +10 -4
- package/dist/chunks/chunk-SPCT7EQA.js.map +7 -0
- package/dist/chunks/{chunk-6EHOJ2CJ.js → chunk-WYS3WVG4.js} +14 -3
- package/dist/chunks/chunk-WYS3WVG4.js.map +7 -0
- package/dist/client.d.ts +11 -1
- package/dist/element.cjs +18 -1
- package/dist/element.cjs.map +2 -2
- package/dist/element.js +4 -4
- package/dist/index.cjs +22 -2
- package/dist/index.cjs.map +2 -2
- package/dist/index.js +4 -4
- package/dist/loader.global.js +2 -2
- package/dist/loader.global.js.map +3 -3
- package/dist/protocol.cjs +5 -2
- package/dist/protocol.cjs.map +2 -2
- package/dist/protocol.d.ts +40 -5
- package/dist/protocol.js +2 -2
- package/dist/react.cjs +14 -1
- package/dist/react.cjs.map +2 -2
- package/dist/react.d.ts +4 -0
- package/dist/react.js +5 -3
- package/dist/react.js.map +2 -2
- package/package.json +1 -1
- package/dist/chunks/chunk-4RT7LXB5.js.map +0 -7
- package/dist/chunks/chunk-6EHOJ2CJ.js.map +0 -7
- package/dist/chunks/chunk-BJF55B44.js.map +0 -7
package/README-CN.md
CHANGED
|
@@ -139,7 +139,7 @@ const { containerRef, client, ready, state } = useXiaochun({ width: 280, height:
|
|
|
139
139
|
|
|
140
140
|
* **Props**:`createXiaochun` 除 `container` 外的全部选项,加上 `onHandshake / onReady / onProgress / onState / onStt / onUtterance / onHitRegion / onMove / onResize / onError / onDestroy`、`className`、`style`、`paused`、`mic`。
|
|
141
141
|
* **重建与热更新**:创建期选项(`src`、`lazy`、`transparent`、`position` 等)变化会重建实例,所以不要在每次渲染里传新值。`width`、`height`、`draggable`、`resizable`(effect 调 `setSize` / `setDraggable` / `setResizable`)、`lang`、`outfit`、`scene`(以及已弃用的 `model`)、`paused`、`mic` 与回调会原地更新,不重建 iframe(由 effect 调 `setOutfit` / `setScene` / `setConfig`)。另有回调 `onOutfitChanged`、`onSceneChanged`。
|
|
142
|
-
* **ref 方法**:`say`、`speakAudio`、`speakAudioStream`、`motion`、`expression`、`lookAt`、`setOutfit`、`setScene`、`getOutfits`、`getScenes`、`prefetch`、`setModel`、`setConfig`、`startListening`、`stopListening`、`mic`、`pause`、`resume`、`activate`、`destroy`,以及 `ready` 与 `instance`。组件挂载前,返回 Promise 的方法会 reject。
|
|
142
|
+
* **ref 方法**:`say`、`speakAudio`、`speakAudioStream`、`setPlaybackRate`、`setVolume`、`motion`、`expression`、`lookAt`、`setOutfit`、`setScene`、`getOutfits`、`getScenes`、`prefetch`、`setModel`、`setConfig`、`startListening`、`stopListening`、`mic`、`pause`、`resume`、`activate`、`destroy`,以及 `ready` 与 `instance`。组件挂载前,返回 Promise 的方法会 reject。
|
|
143
143
|
|
|
144
144
|
---
|
|
145
145
|
|
|
@@ -152,14 +152,18 @@ await xc.speakAudio(arrayBuffer, { text: '你好', motion: true, lipsync: true }
|
|
|
152
152
|
await xc.speakAudio(blob); // Blob(mp3 / wav / ogg 等)
|
|
153
153
|
await xc.speakAudio('https://cdn.example.com/voice.mp3'); // URL:默认由宿主页 fetch({ fetch: 'frame' } = 交给 iframe 去 fetch)
|
|
154
154
|
await xc.speakAudio(pcm, { format: 'pcm16', sampleRate: 24000 }); // 无头原始 PCM 必须给 sampleRate(8000–96000)
|
|
155
|
-
await xc.speakAudio(pcm, { format: 'pcm16', sampleRate: 16000, audible: false }); // 只做动作和口型, iframe 增益为 0
|
|
155
|
+
await xc.speakAudio(pcm, { format: 'pcm16', sampleRate: 16000, audible: false, playbackRate: 1.5, volume: 0.8 }); // 只做动作和口型, iframe 增益为 0
|
|
156
|
+
xc.setPlaybackRate(1.25); // 播放中改宿主音频倍速, 播放头不跳
|
|
157
|
+
xc.setVolume(0.6); // 0~1; audible 为 false 时扬声器仍然没声音
|
|
156
158
|
|
|
157
|
-
const s = xc.speakAudioStream({ sampleRate: 24000 });
|
|
159
|
+
const s = xc.speakAudioStream({ sampleRate: 24000, playbackRate: 1.25 }); // 流式:比如 TTS 服务边合成边返回的 PCM 块
|
|
158
160
|
s.write(int16Chunk); s.write(next); s.end(); await s.done; // s.abort() 立即停止
|
|
159
161
|
// 随时可取消:传 { signal: abortController.signal }
|
|
160
162
|
```
|
|
161
163
|
|
|
162
164
|
* `audible` 默认 `true`。`false` 仍会解码、生成动作并驱动口型,但 iframe 增益为 0,声音可以由宿主页面自己播放。
|
|
165
|
+
* `playbackRate` 默认 `1`,夹到 `0.25~3`。`volume` 默认 `1`,夹到 `0~1`。两者同时作用于这次宿主音频和它的动作时钟。`setPlaybackRate()` / `setVolume()` 在播放中修改,不重开;改倍速时播放头不跳。省略则沿用上一次的值。TTS(`say`)仍是 1 倍、满音量。`audible: false` 时扬声器增益保持 0。`pause()` 只暂停渲染,语音和动作时钟继续走。
|
|
166
|
+
* 宿主音频的 `utterance start` 在思考动作和 EMAGE 首窗之后、声音时钟起步时才发。页面自己的播放器要等这个事件再出声,口型才对齐。
|
|
163
167
|
* 失败时 reject:`bad_request`(无法解码 / 空音频 / URL 或 sampleRate 不合法)、`unsupported`、`failed`。
|
|
164
168
|
* 浏览器仍要求宿主页先有用户手势才能出声,iframe 也要有 `allow="autoplay"`(SDK 已自动设置)。
|
|
165
169
|
* 新的 `say()` / `speakAudio()` 会打断正在进行的那一次,被打断的 Promise 会 resolve。
|
|
@@ -387,7 +391,7 @@ createXiaochun({
|
|
|
387
391
|
| `crossOriginIsolated` | `false` | 给 iframe 的 `allow` 追加 `cross-origin-isolated`(默认仍是 `microphone; autoplay`)。要求宿主页自己已跨源隔离,见 [可选:跨源隔离](#可选跨源隔离让-emage-用多线程) |
|
|
388
392
|
| `zIndex` | `2147483000` | 悬浮模式层级(`--xc-z-index` 变量优先) |
|
|
389
393
|
|
|
390
|
-
**实例**:`ready` · `say(text, { mode: 'speak' \| 'chat' })` · `speakAudio(source, opts)` · `speakAudioStream(opts)` · `motion(nameOrUrlOrOptions)` · `expression(name)` · `setOutfit(id)` · `setScene(id)` · `getOutfits()` · `getScenes()` · `prefetch(ids?)` · `outfit` / `scene`(只读)· `setModel(outfitOrUrl)` *(旧)* · `setConfig(cfg)` · `setSize(w, h)` · `getBox()` · `setDraggable(on)` · `setResizable(on | limits)` · `startListening()` / `stopListening()` / `mic(on)` · `pause()` / `resume()` · `activate()` · `destroy()` · `on(event, cb)` · `lookAt()` *(协议已预留,目前返回 `unsupported`)*。
|
|
394
|
+
**实例**:`ready` · `say(text, { mode: 'speak' \| 'chat' })` · `speakAudio(source, opts)` · `speakAudioStream(opts)` · `setPlaybackRate(rate)` · `setVolume(volume)` · `motion(nameOrUrlOrOptions)` · `expression(name)` · `setOutfit(id)` · `setScene(id)` · `getOutfits()` · `getScenes()` · `prefetch(ids?)` · `outfit` / `scene`(只读)· `setModel(outfitOrUrl)` *(旧)* · `setConfig(cfg)` · `setSize(w, h)` · `getBox()` · `setDraggable(on)` · `setResizable(on | limits)` · `startListening()` / `stopListening()` / `mic(on)` · `pause()` / `resume()` · `activate()` · `destroy()` · `on(event, cb)` · `lookAt()` *(协议已预留,目前返回 `unsupported`)*。
|
|
391
395
|
|
|
392
396
|
**事件**:`handshake` · `ready` · `progress` · `state` · `stt` · `utterance`(`phase: 'start' | 'end'`,`kind: 'text' | 'audio'`)· `hit-region` · `outfit-changed` · `scene-changed` · `lang-changed`(`{ lang, previous?, initial? }`)· `move` / `resize`(`{ phase: 'start' | 'move' | 'end', left, top, width, height }`)· `error`(新增 `busy`、`unknown_id`;旧 iframe 上开手势会收到一次 `unsupported`)· `destroy`。`progress.phase` 为 `'model' | 'outfit' | 'prefetch'`。
|
|
393
397
|
|
|
@@ -419,7 +423,7 @@ createXiaochun({
|
|
|
419
423
|
| `cross-origin-isolated` | `false` | 同 `createXiaochun({ crossOriginIsolated })`;修改会重建 iframe |
|
|
420
424
|
|
|
421
425
|
**事件**(`CustomEvent`,`composed`,`detail` = 协议 payload):`xc-ready` · `xc-progress` · `xc-state` · `xc-stt` · `xc-utterance` · `xc-outfit-changed` · `xc-scene-changed` · `xc-lang-changed` · `xc-move` · `xc-resize` · `xc-error`。
|
|
422
|
-
**方法**:`say` · `speakAudio` · `speakAudioStream` · `motion` · `expression` · `setOutfit` · `setScene` · `getOutfits` · `getScenes` · `prefetch` · `destroy`;`el.client` 可拿到完整的 SDK 实例。
|
|
426
|
+
**方法**:`say` · `speakAudio` · `speakAudioStream` · `setPlaybackRate` · `setVolume` · `motion` · `expression` · `setOutfit` · `setScene` · `getOutfits` · `getScenes` · `prefetch` · `destroy`;`el.client` 可拿到完整的 SDK 实例。
|
|
423
427
|
|
|
424
428
|
---
|
|
425
429
|
|
package/README.md
CHANGED
|
@@ -139,7 +139,7 @@ const { containerRef, client, ready, state } = useXiaochun({ width: 280, height:
|
|
|
139
139
|
|
|
140
140
|
* **Props**: every `createXiaochun` option except `container`, plus `onHandshake / onReady / onProgress / onState / onStt / onUtterance / onHitRegion / onMove / onResize / onError / onDestroy`, `className`, `style`, `paused`, and `mic`.
|
|
141
141
|
* **Rebuild vs. live update**: creation-time options (`src`, `lazy`, `transparent`, `position`, …) rebuild the instance when they change, so avoid passing fresh values on every render. `width`, `height`, `draggable`, `resizable` (effects call `setSize` / `setDraggable` / `setResizable`), `lang`, `outfit`, `scene` (and the deprecated `model`), `paused`, `mic` and the callbacks update in place without recreating the iframe (effects call `setOutfit` / `setScene` / `setConfig`). Also available: `onOutfitChanged`, `onSceneChanged`.
|
|
142
|
-
* **Ref methods**: `say`, `speakAudio`, `speakAudioStream`, `motion`, `expression`, `lookAt`, `setOutfit`, `setScene`, `getOutfits`, `getScenes`, `prefetch`, `setModel`, `setConfig`, `startListening`, `stopListening`, `mic`, `pause`, `resume`, `activate`, `destroy`, plus `ready` and `instance`. Methods that return a Promise reject until the component is mounted.
|
|
142
|
+
* **Ref methods**: `say`, `speakAudio`, `speakAudioStream`, `setPlaybackRate`, `setVolume`, `motion`, `expression`, `lookAt`, `setOutfit`, `setScene`, `getOutfits`, `getScenes`, `prefetch`, `setModel`, `setConfig`, `startListening`, `stopListening`, `mic`, `pause`, `resume`, `activate`, `destroy`, plus `ready` and `instance`. Methods that return a Promise reject until the component is mounted.
|
|
143
143
|
|
|
144
144
|
---
|
|
145
145
|
|
|
@@ -152,14 +152,18 @@ await xc.speakAudio(arrayBuffer, { text: 'Hi', motion: true, lipsync: true }); /
|
|
|
152
152
|
await xc.speakAudio(blob); // Blob (mp3 / wav / ogg …)
|
|
153
153
|
await xc.speakAudio('https://cdn.example.com/voice.mp3'); // URL: fetched by the host page by default ({ fetch: 'frame' } = fetched inside the iframe)
|
|
154
154
|
await xc.speakAudio(pcm, { format: 'pcm16', sampleRate: 24000 }); // headerless PCM needs sampleRate (8000–96000)
|
|
155
|
-
await xc.speakAudio(pcm, { format: 'pcm16', sampleRate: 16000, audible: false }); // motion + lip-sync only; iframe gain is 0
|
|
155
|
+
await xc.speakAudio(pcm, { format: 'pcm16', sampleRate: 16000, audible: false, playbackRate: 1.5, volume: 0.8 }); // motion + lip-sync only; iframe gain is 0
|
|
156
|
+
xc.setPlaybackRate(1.25); // live host-audio rate; the playhead does not jump
|
|
157
|
+
xc.setVolume(0.6); // 0–1; speakers stay silent while audible is false
|
|
156
158
|
|
|
157
|
-
const s = xc.speakAudioStream({ sampleRate: 24000 });
|
|
159
|
+
const s = xc.speakAudioStream({ sampleRate: 24000, playbackRate: 1.25 }); // streaming: e.g. PCM chunks from a TTS server
|
|
158
160
|
s.write(int16Chunk); s.write(next); s.end(); await s.done; // s.abort() stops immediately
|
|
159
161
|
// Cancel any time: pass { signal: abortController.signal }
|
|
160
162
|
```
|
|
161
163
|
|
|
162
164
|
* `audible` defaults to `true`. `false` still decodes the audio, generates motion, and drives lip-sync, but the iframe gain is 0 so the host page can be the only speaker.
|
|
165
|
+
* `playbackRate` defaults to `1` and is clamped to `0.25–3`. `volume` defaults to `1` and is clamped to `0–1`. Both scale this host-audio utterance and its motion clock. `setPlaybackRate()` / `setVolume()` change them mid-playback without restarting; a rate change does not jump the playhead. Omitted values keep the last setting. TTS (`say`) stays at 1x and full volume. `audible: false` keeps the speaker gain at 0. `pause()` only suspends rendering; speech and the motion clock keep running.
|
|
166
|
+
* For host audio, `utterance` `start` fires when the audio clock starts, after the thinking pose and the first EMAGE window. Hold the page's own player until that event if it must stay in sync.
|
|
163
167
|
* Rejects with `bad_request` (undecodable / empty audio, bad URL or sampleRate), `unsupported`, or `failed`.
|
|
164
168
|
* Browsers still require a user gesture on the host page before audio can play, and the iframe needs `allow="autoplay"` (the SDK sets it).
|
|
165
169
|
* A newer `say()` / `speakAudio()` call preempts the one in progress, and the preempted promise resolves.
|
|
@@ -387,7 +391,7 @@ createXiaochun({
|
|
|
387
391
|
| `crossOriginIsolated` | `false` | Append `cross-origin-isolated` to the iframe `allow` (default stays `microphone; autoplay`). Needs a host page that is itself isolated; see [Opt-in: cross-origin isolation](#opt-in-cross-origin-isolation-multi-threaded-emage) |
|
|
388
392
|
| `zIndex` | `2147483000` | Floating mode layer (the `--xc-z-index` variable takes precedence) |
|
|
389
393
|
|
|
390
|
-
**Instance**: `ready` · `say(text, { mode: 'speak' \| 'chat' })` · `speakAudio(source, opts)` · `speakAudioStream(opts)` · `motion(nameOrUrlOrOptions)` · `expression(name)` · `setOutfit(id)` · `setScene(id)` · `getOutfits()` · `getScenes()` · `prefetch(ids?)` · `outfit` / `scene` (getters) · `setModel(outfitOrUrl)` *(legacy)* · `setConfig(cfg)` · `setSize(w, h)` · `getBox()` · `setDraggable(on)` · `setResizable(on | limits)` · `startListening()` / `stopListening()` / `mic(on)` · `pause()` / `resume()` · `activate()` · `destroy()` · `on(event, cb)` · `lookAt()` *(reserved in the protocol; currently returns `unsupported`)*.
|
|
394
|
+
**Instance**: `ready` · `say(text, { mode: 'speak' \| 'chat' })` · `speakAudio(source, opts)` · `speakAudioStream(opts)` · `setPlaybackRate(rate)` · `setVolume(volume)` · `motion(nameOrUrlOrOptions)` · `expression(name)` · `setOutfit(id)` · `setScene(id)` · `getOutfits()` · `getScenes()` · `prefetch(ids?)` · `outfit` / `scene` (getters) · `setModel(outfitOrUrl)` *(legacy)* · `setConfig(cfg)` · `setSize(w, h)` · `getBox()` · `setDraggable(on)` · `setResizable(on | limits)` · `startListening()` / `stopListening()` / `mic(on)` · `pause()` / `resume()` · `activate()` · `destroy()` · `on(event, cb)` · `lookAt()` *(reserved in the protocol; currently returns `unsupported`)*.
|
|
391
395
|
|
|
392
396
|
**Events**: `handshake` · `ready` · `progress` · `state` · `stt` · `utterance` (`phase: 'start' | 'end'`, `kind: 'text' | 'audio'`) · `hit-region` · `outfit-changed` · `scene-changed` · `lang-changed` (`{ lang, previous?, initial? }`) · `move` / `resize` (`{ phase: 'start' | 'move' | 'end', left, top, width, height }`) · `error` (now also `busy`, `unknown_id`; one `unsupported` if gestures are enabled on an old iframe) · `destroy`. `progress.phase` is `'model' | 'outfit' | 'prefetch'`.
|
|
393
397
|
|
|
@@ -419,7 +423,7 @@ createXiaochun({
|
|
|
419
423
|
| `cross-origin-isolated` | `false` | Same as `createXiaochun({ crossOriginIsolated })`; changing it rebuilds the iframe |
|
|
420
424
|
|
|
421
425
|
**Events** (`CustomEvent`, `composed`, `detail` = protocol payload): `xc-ready` · `xc-progress` · `xc-state` · `xc-stt` · `xc-utterance` · `xc-outfit-changed` · `xc-scene-changed` · `xc-lang-changed` · `xc-move` · `xc-resize` · `xc-error`.
|
|
422
|
-
**Methods**: `say` · `speakAudio` · `speakAudioStream` · `motion` · `expression` · `setOutfit` · `setScene` · `getOutfits` · `getScenes` · `prefetch` · `destroy`; `el.client` gives you the full SDK instance.
|
|
426
|
+
**Methods**: `say` · `speakAudio` · `speakAudioStream` · `setPlaybackRate` · `setVolume` · `motion` · `expression` · `setOutfit` · `setScene` · `getOutfits` · `getScenes` · `prefetch` · `destroy`; `el.client` gives you the full SDK instance.
|
|
423
427
|
|
|
424
428
|
---
|
|
425
429
|
|
package/dist/avatar-element.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* 可用 ::part() 定制外壳: ::part(mount) ::part(wrapper) ::part(iframe) ::part(placeholder) (iframe 内部不可被宿主 CSS 影响)
|
|
22
22
|
* 事件 (CustomEvent, composed, detail = 协议 payload): xc-ready(模型加载完) xc-progress xc-state xc-stt xc-utterance xc-error xc-outfit-changed xc-scene-changed xc-lang-changed
|
|
23
23
|
* xc-move / xc-resize (用户拖动 / 缩放, detail = {phase, left, top, width, height})
|
|
24
|
-
* 方法: say(text) speakAudio(source, opts) speakAudioStream(opts) motion(m) expression(name) setOutfit(id) setScene(id) getOutfits() getScenes() destroy() (+ client 属性拿到完整 SDK 实例)
|
|
24
|
+
* 方法: say(text) speakAudio(source, opts) speakAudioStream(opts) setPlaybackRate(rate) setVolume(volume) motion(m) expression(name) setOutfit(id) setScene(id) getOutfits() getScenes() destroy() (+ client 属性拿到完整 SDK 实例)
|
|
25
25
|
*/
|
|
26
26
|
import { type XiaochunAudioOptions, type XiaochunAudioSource, type XiaochunAudioStream, type XiaochunInstance } from './client';
|
|
27
27
|
import type { XcExpressionPayload, XcMotionPayload, XcOutfitInfo, XcPrefetchedPayload, XcSceneInfo } from './protocol';
|
|
@@ -36,6 +36,8 @@ export interface XiaochunAvatarElement extends HTMLElement {
|
|
|
36
36
|
speakAudioStream(opts: XiaochunAudioOptions & {
|
|
37
37
|
sampleRate: number;
|
|
38
38
|
}): XiaochunAudioStream;
|
|
39
|
+
setPlaybackRate(rate: number): void;
|
|
40
|
+
setVolume(volume: number): void;
|
|
39
41
|
motion(m: XcMotionPayload | string): Promise<void>;
|
|
40
42
|
expression(name: XcExpressionPayload['name']): Promise<void>;
|
|
41
43
|
/** 换内置服装 (串行 + last-wins, 见 XiaochunInstance.setOutfit)。 */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @firetable/project-xiaochun v0.1.
|
|
1
|
+
/*! @firetable/project-xiaochun v0.1.19 | MIT | https://github.com/FireTable/project-xiaochun */
|
|
2
2
|
|
|
3
3
|
// src/protocol.ts
|
|
4
4
|
var XC_PROTOCOL_VERSION = 1;
|
|
@@ -20,6 +20,7 @@ var XC_HOST_TO_FRAME = [
|
|
|
20
20
|
"xc.prefetch",
|
|
21
21
|
"xc.setConfig",
|
|
22
22
|
"xc.mic",
|
|
23
|
+
"xc.transport",
|
|
23
24
|
"xc.pause",
|
|
24
25
|
"xc.resume",
|
|
25
26
|
"xc.destroy"
|
|
@@ -54,6 +55,7 @@ var XC_IMPLEMENTED_COMMANDS = [
|
|
|
54
55
|
"xc.prefetch",
|
|
55
56
|
"xc.setConfig",
|
|
56
57
|
"xc.mic",
|
|
58
|
+
"xc.transport",
|
|
57
59
|
"xc.pause",
|
|
58
60
|
"xc.resume",
|
|
59
61
|
"xc.destroy"
|
|
@@ -64,7 +66,8 @@ var XC_PROTOCOL_MAPPING = [
|
|
|
64
66
|
{ xc: "xc.say", note: 'mode:"chat" (\u8D70 LLM)', action: null, url: null },
|
|
65
67
|
{ xc: "xc.audio", note: "source \u4E3A URL \u5B57\u7B26\u4E32", action: "speak", url: "xiaochun://speak?audioUrl=\u2026[&text=\u2026]" },
|
|
66
68
|
{ xc: "xc.audio", note: "source \u4E3A ArrayBuffer / Blob", action: "audio", url: null },
|
|
67
|
-
{ xc: "xc.audio.chunk / xc.audio.end", note: "\u6D41\u5F0F PCM", action: "audio", url: null }
|
|
69
|
+
{ xc: "xc.audio.chunk / xc.audio.end", note: "\u6D41\u5F0F PCM", action: "audio", url: null },
|
|
70
|
+
{ xc: "xc.transport", note: "\u6539\u5BBF\u4E3B\u97F3\u9891\u500D\u901F / \u97F3\u91CF, \u65E0 deep link", action: null, url: null }
|
|
68
71
|
];
|
|
69
72
|
var XC_WINDOW_CORNER_RADIUS = 20;
|
|
70
73
|
var XC_UI_PARTS = ["chat", "bubble", "outfit", "scene", "lang", "github"];
|
|
@@ -219,4 +222,4 @@ export {
|
|
|
219
222
|
isXcEnvelope,
|
|
220
223
|
normalizeOrigin
|
|
221
224
|
};
|
|
222
|
-
//# sourceMappingURL=chunk-
|
|
225
|
+
//# sourceMappingURL=chunk-RTBOYZAZ.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/protocol.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * protocol.ts \u2014 Project XiaoChun `<iframe>` \u5D4C\u5165\u534F\u8BAE (\u5E38\u91CF + \u7C7B\u578B, \u96F6\u8FD0\u884C\u65F6\u4F9D\u8D56)\u3002\n *\n * \u5355\u4E00\u4E8B\u5B9E\u6E90: SDK (\u672C\u5305) \u4E0E `/embed` \u9875\u9762 (\u4E3B\u4ED3\u5E93 src/embed/) \u5171\u7528\u8FD9\u4E00\u4EFD\u3002\n * \u6240\u6709\u6D88\u606F\u540D\u7EDF\u4E00 `xc.` \u524D\u7F00; \u4FE1\u5C01 `{ type, v, id?, payload? }`\u3002\n *\n * \u901A\u9053:\n * 1. \u63E1\u624B: iframe \u2192 \u5BBF\u4E3B `xc.ready` (window.postMessage, targetOrigin = \u5BBF\u4E3B origin)\n * \u5BBF\u4E3B \u2192 iframe `xc.init` (window.postMessage, targetOrigin = iframe origin, \u8F6C\u79FB MessagePort)\n * 2. \u63E1\u624B\u540E\u5168\u90E8\u8D70 MessageChannel \u7AEF\u53E3 (\u7AEF\u53E3\u672C\u8EAB\u5373\u51ED\u8BC1, \u4E0D\u518D\u4F9D\u8D56 `*`)\n */\n\n/** \u534F\u8BAE\u7248\u672C; \u4E0D\u517C\u5BB9\u53D8\u66F4\u65F6 +1\u3002 */\nexport const XC_PROTOCOL_VERSION = 1 as const;\n\n/** \u5B98\u65B9\u90E8\u7F72\u7684 origin / embed \u5165\u53E3\u3002 */\nexport const XC_DEFAULT_ORIGIN = 'https://xiaochun.firetable.tech';\nexport const XC_DEFAULT_SRC = `${XC_DEFAULT_ORIGIN}/embed`;\n\n/** \u5BBF\u4E3B \u2192 iframe \u547D\u4EE4\u3002 */\nexport const XC_HOST_TO_FRAME = [\n 'xc.init',\n 'xc.say',\n 'xc.audio',\n 'xc.audio.chunk',\n 'xc.audio.end',\n 'xc.motion',\n 'xc.expression',\n 'xc.lookAt',\n 'xc.pointer',\n 'xc.setModel',\n 'xc.setOutfit',\n 'xc.setScene',\n 'xc.prefetch',\n 'xc.setConfig',\n 'xc.mic',\n 'xc.transport',\n 'xc.pause',\n 'xc.resume',\n 'xc.destroy',\n] as const;\n\n/** iframe \u2192 \u5BBF\u4E3B \u4E8B\u4EF6\u3002 */\nexport const XC_FRAME_TO_HOST = [\n 'xc.ready',\n 'xc.load.progress',\n 'xc.loaded',\n 'xc.outfit-changed',\n 'xc.scene-changed',\n 'xc.lang-changed',\n 'xc.prefetched',\n 'xc.state',\n 'xc.stt',\n 'xc.utterance',\n 'xc.hit-region',\n 'xc.gesture-move',\n 'xc.gesture-resize',\n 'xc.error',\n] as const;\n\nexport type XcHostMessageType = (typeof XC_HOST_TO_FRAME)[number];\nexport type XcFrameMessageType = (typeof XC_FRAME_TO_HOST)[number];\n\n/** \u5F53\u524D /embed \u5DF2\u5B9E\u73B0\u7684\u547D\u4EE4 (xc.init \u662F\u63E1\u624B, \u4E0D\u8BA1\u5165)\u3002 */\nexport const XC_IMPLEMENTED_COMMANDS = [\n 'xc.say',\n 'xc.audio',\n 'xc.audio.chunk',\n 'xc.audio.end',\n 'xc.motion',\n 'xc.expression',\n 'xc.pointer',\n 'xc.setModel',\n 'xc.setOutfit',\n 'xc.setScene',\n 'xc.prefetch',\n 'xc.setConfig',\n 'xc.mic',\n 'xc.transport',\n 'xc.pause',\n 'xc.resume',\n 'xc.destroy',\n] as const satisfies readonly XcHostMessageType[];\n\n/**\n * \u6682\u4E0D\u652F\u6301 (\u5E95\u5C42\u65E0\u5BF9\u5E94\u80FD\u529B), /embed \u56DE `xc.error{code:'unsupported'}`\u3002\n * TODO(xc.lookAt): \u89C6\u7EBF\u76EE\u524D\u7531\u76F8\u673A + \u968F\u673A\u626B\u89C6\u9A71\u52A8 (GazeController), \u6CA1\u6709\"\u5916\u90E8\u6307\u5B9A\u6CE8\u89C6\u70B9\"\u7684\u5165\u53E3;\n * \u9700\u8981\u5148\u5728 gaze \u5C42\u52A0 overrideTarget \u518D\u5F00\u653E\u3002\n */\nexport const XC_UNSUPPORTED_COMMANDS = ['xc.lookAt'] as const satisfies readonly XcHostMessageType[];\n\n/**\n * xc.* \u2194 xiaochun:// \u5BF9\u7167\u8868 (\u540C\u4E00\u8BED\u4E49\u7684\u4E24\u4E2A\u4F20\u8F93\u5C42, \u4E3B\u5E94\u7528\u5185\u8D70\u540C\u4E00\u4E2A handler: src/core/protocol/handler.ts)\u3002\n * - `action`: \u5185\u90E8 protocol action \u5BF9\u8C61 (src/core/protocol/types.ts); null = \u6CA1\u6709 deep link \u5BF9\u5E94\u7269\u3002\n * - `url`: \u5BF9\u5E94\u7684 deep link \u5F62\u5F0F (OS \u7EA7, \u53EA\u5728 Tauri \u58F3\u751F\u6548; iframe \u4E0D\u4F1A\u54CD\u5E94 xiaochun://)\u3002\n */\nexport const XC_PROTOCOL_MAPPING = [\n { xc: 'xc.say', note: 'mode:\"speak\" (\u9ED8\u8BA4)', action: 'speak', url: 'xiaochun://speak?text=\u2026' },\n { xc: 'xc.say', note: 'mode:\"chat\" (\u8D70 LLM)', action: null, url: null },\n { xc: 'xc.audio', note: 'source \u4E3A URL \u5B57\u7B26\u4E32', action: 'speak', url: 'xiaochun://speak?audioUrl=\u2026[&text=\u2026]' },\n { xc: 'xc.audio', note: 'source \u4E3A ArrayBuffer / Blob', action: 'audio', url: null },\n { xc: 'xc.audio.chunk / xc.audio.end', note: '\u6D41\u5F0F PCM', action: 'audio', url: null },\n { xc: 'xc.transport', note: '\u6539\u5BBF\u4E3B\u97F3\u9891\u500D\u901F / \u97F3\u91CF, \u65E0 deep link', action: null, url: null },\n] as const;\n\nexport type XcErrorCode =\n | 'unsupported' // \u547D\u4EE4/\u53C2\u6570\u5B58\u5728\u4F46\u5E95\u5C42\u6682\u65E0\u80FD\u529B\n | 'bad_request' // \u53C2\u6570\u7F3A\u5931/\u975E\u6CD5\n | 'not_ready' // \u6A21\u578B\u5C1A\u672A\u52A0\u8F7D\u5B8C\u6210\n | 'busy' // \u88AB\u540C\u7C7B\u66F4\u65B0\u7684\u8BF7\u6C42\u53D6\u4EE3 (\u6362\u88C5 last-wins: \u6392\u961F\u4E2D\u7684\u65E7\u8BF7\u6C42\u88AB\u65B0\u8BF7\u6C42\u9876\u6389); \u53EF\u5FFD\u7565, \u4EE5\u6700\u65B0\u4E00\u6B21\u4E3A\u51C6\n | 'unknown_id' // outfit / scene id \u4E0D\u5728 capabilities \u5217\u8868\u91CC (\u542B\u539F\u578B\u952E\u5982 constructor)\n | 'origin_denied' // \u63E1\u624B\u6765\u6E90\u4E0D\u5728\u767D\u540D\u5355\n | 'failed'; // \u6267\u884C\u671F\u5F02\u5E38\n\n/**\n * outfit / scene id \u7684\u7EDF\u4E00\u683C\u5F0F: \u5C0F\u5199\u5B57\u6BCD\u5F00\u5934, \u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF, \u6700\u957F 64\u3002\n * \u6240\u6709 id \u5165\u53E3 (xc.setOutfit / xc.setScene / ?outfit= / ?scene=) \u90FD\u5148\u8FC7\u8FD9\u6761\u6B63\u5219, \u518D\u7528 hasOwnProperty \u67E5\u8868,\n * \u8FD9\u6837 `__proto__` / `constructor` / `toString` \u8FD9\u7C7B\u539F\u578B\u952E\u6C38\u8FDC\u4E0D\u4F1A\u88AB\u5F53\u6210\u5408\u6CD5 id\u3002\n */\n/**\n * iframe \u5185\u7F6E\u754C\u9762\u90E8\u4EF6\u540D (`?ui=chat,outfit,scene,lang,github` / SDK `ui: [...]`)\u3002\n * chat \u5E95\u90E8\u804A\u5929\u680F (ChatBar, \u5BF9\u8BDD\u8D70 WebLLM)\n * bubble \u5934\u9876\u6C14\u6CE1 (\u8BF4\u8BDD\u6587\u672C / \u72B6\u6001)\n * outfit \u6362\u88C5\u6309\u94AE (\u4E0E\u4E3B\u7AD9 TopHeader \u540C\u6B3E)\n * scene \u6362\u573A\u666F(\u80CC\u666F)\u6309\u94AE (\u4E0E\u4E3B\u7AD9 TopHeader \u540C\u6B3E)\n * lang \u8BED\u8A00\u5207\u6362\u6309\u94AE (\u4E2D\u6587 / English / \u65E5\u672C\u8A9E; \u4E0E\u4E3B\u7AD9 TopHeader \u540C\u4E00\u4E2A\u7EC4\u4EF6)\n * github GitHub \u94FE\u63A5\u6309\u94AE (\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u9879\u76EE\u4ED3\u5E93; \u4E0E\u4E3B\u7AD9 TopHeader \u540C\u4E00\u4E2A\u7EC4\u4EF6)\n */\n/**\n * \u7A97\u53E3\u5706\u89D2\u534A\u5F84 (CSS px)\u3002Tauri \u684C\u5BA0\u7684\u65E0\u6846\u7A97\u53E3\u5706\u89D2 (src/styles/main.css \u7684 --xc-window-radius) \u4E0E /embed \u975E\u900F\u660E\u573A\u666F\u7684\u9ED8\u8BA4\u5916\u58F3\u5706\u89D2\u5171\u7528\u8FD9\u4E00\u4E2A\u503C,\n * \u6709\u5355\u6D4B\u4FDD\u8BC1\u4E24\u5904\u4E0D\u6F02\u79FB\u3002\u89D2\u843D\u5F27\u7EBF (components/CornerHandle) \u7684\u51E0\u4F55\u5C31\u662F\u6309\u8FD9\u4E2A\u534A\u5F84\u753B\u7684\u3002\n */\nexport const XC_WINDOW_CORNER_RADIUS = 20;\n\nexport const XC_UI_PARTS = ['chat', 'bubble', 'outfit', 'scene', 'lang', 'github'] as const;\nexport type XcUiPart = (typeof XC_UI_PARTS)[number];\nconst XC_UI_PART_SET: ReadonlySet<string> = new Set(XC_UI_PARTS);\n/** 0.1.14 \u91CC `ui=1` / `ui: true` \u7684\u542B\u4E49 = \u5F53\u65F6\u5B58\u5728\u7684\u5168\u90E8\u90E8\u4EF6\u3002 */\nexport const XC_UI_LEGACY_PARTS: readonly XcUiPart[] = ['chat', 'bubble'];\n\nexport interface XcUiParse {\n /** \u767D\u540D\u5355\u5185\u7684\u90E8\u4EF6 (\u53BB\u91CD, \u4FDD\u6301 XC_UI_PARTS \u987A\u5E8F)\u3002 */\n parts: XcUiPart[];\n /** \u88AB\u5FFD\u7565\u7684\u672A\u77E5\u9879 (\u8C03\u7528\u65B9\u8D1F\u8D23 warn)\u3002 */\n unknown: string[];\n /** \u7528\u4E86\u5DF2\u5F03\u7528\u7684 1 / true \u5199\u6CD5 (\u8C03\u7528\u65B9\u8D1F\u8D23 warn)\u3002 */\n legacy: boolean;\n}\n\n/** \u89C4\u6574\u90E8\u4EF6\u540D\u5217\u8868: \u975E\u5B57\u7B26\u4E32 / \u672A\u77E5\u540D\u5B57\u5FFD\u7565\u5E76\u8BB0\u5165 unknown, \u91CD\u590D\u53BB\u91CD\u3002 */\nexport function parseXcUiList(items: readonly unknown[]): XcUiParse {\n const seen = new Set<string>();\n const unknown: string[] = [];\n for (const it of items) {\n const name = typeof it === 'string' ? it.trim().toLowerCase() : '';\n if (XC_UI_PART_SET.has(name)) seen.add(name);\n else unknown.push(typeof it === 'string' ? it.slice(0, 40) : String(typeof it));\n }\n return { parts: XC_UI_PARTS.filter((p) => seen.has(p)), unknown, legacy: false };\n}\n\n/** `?ui=` \u53C2\u6570 / \u5143\u7D20\u5C5E\u6027: \u9017\u53F7\u5206\u9694\u7684\u90E8\u4EF6\u540D; \u7F3A\u7701 / \u7A7A / 0 / false = \u5168\u4E0D\u663E\u793A; 1 / true = \u65E7\u5199\u6CD5 (\u5F03\u7528, \u6620\u5C04\u4E3A chat + bubble)\u3002 */\nexport function parseXcUiParam(raw: string | null | undefined): XcUiParse {\n const v = (raw ?? '').trim().toLowerCase();\n if (v === '' || v === '0' || v === 'false') return { parts: [], unknown: [], legacy: false };\n if (v === '1' || v === 'true') return { parts: [...XC_UI_LEGACY_PARTS], unknown: [], legacy: true };\n return parseXcUiList(v.split(',').filter((x) => x.trim() !== ''));\n}\n\n/** SDK \u9009\u9879 `ui` (\u6570\u7EC4, \u6216\u5F03\u7528\u7684\u5E03\u5C14) \u2192 \u89C4\u6574\u7ED3\u679C\u3002 */\nexport function normalizeXcUiOption(v: unknown): XcUiParse {\n if (v === true) return { parts: [...XC_UI_LEGACY_PARTS], unknown: [], legacy: true };\n if (Array.isArray(v)) return parseXcUiList(v);\n return { parts: [], unknown: [], legacy: false };\n}\n\n/**\n * \u5185\u7F6E\u754C\u9762 (\u9876\u680F\u6309\u94AE outfit/scene/lang/github + \u804A\u5929\u680F chat) \u7684\u663E\u793A\u7B56\u7565, \u4E0E Tauri \u684C\u5BA0\u7684 \"\u70B9\u51FB\u89D2\u8272\u624D\u51FA\u73B0\" \u540C\u4E00\u5957\u72B6\u6001\u673A (src/hooks/usePetUiVisibility.ts):\n * 'transparent' (\u9ED8\u8BA4) \u4E0E Tauri \u5B8C\u5168\u4E00\u81F4: \u53EA\u6709\u900F\u660E\u573A\u666F\u9ED8\u8BA4\u9690\u85CF, \u5355\u51FB\u89D2\u8272\u51FA\u73B0 (\u6309\u4F4F\u62D6\u52A8 / \u957F\u6309\u62D6\u4E0D\u7B97), \u518D\u5355\u51FB\u89D2\u8272\u6536\u8D77, \u5355\u51FB\u7A7A\u767D\u6536\u8D77, 10 \u79D2\u65E0\u64CD\u4F5C\u81EA\u52A8\u6536\u8D77\n * (\u60AC\u505C\u5728\u6309\u94AE / \u804A\u5929\u680F\u4E0A\u3001\u83DC\u5355\u6216\u5BF9\u8BDD\u6846\u6253\u5F00\u3001\u8F93\u5165\u6846\u805A\u7126 / \u6709\u8F93\u5165 / \u53D1\u9001\u4E2D \u65F6\u4E0D\u6536); \u4EAE / \u6697\u573A\u666F\u5E38\u663E\u3002\n * true \u6240\u6709\u573A\u666F\u90FD\u6309\u4E0A\u9762\u7684\u70B9\u51FB\u51FA\u73B0\u89C4\u5219 (\u4EAE / \u6697\u573A\u666F\u4E5F\u9ED8\u8BA4\u9690\u85CF)\u3002\n * false \u4E00\u76F4\u663E\u793A (\u5FFD\u7565\u70B9\u51FB\u89C4\u5219)\u3002\n */\nexport type XcUiAutoHide = boolean | 'transparent';\n/** \u9ED8\u8BA4\u503C: 'transparent' (\u4E0E Tauri \u4E00\u81F4: \u53EA\u6709\u900F\u660E\u573A\u666F\u70B9\u51FB\u51FA\u73B0)\u3002 */\nexport const XC_UI_AUTOHIDE_DEFAULT: XcUiAutoHide = 'transparent';\n/** `?uiAutoHide=` \u53C2\u6570 / \u5143\u7D20\u5C5E\u6027: 1|true \u2192 true; 0|false \u2192 false; transparent \u2192 'transparent'; \u7F3A\u7701 / \u975E\u6CD5 \u2192 undefined (\u8C03\u7528\u65B9\u7528\u9ED8\u8BA4\u503C)\u3002 */\nexport function parseXcUiAutoHide(raw: unknown): XcUiAutoHide | undefined {\n if (raw === true || raw === false || raw === 'transparent') return raw;\n if (typeof raw !== 'string') return undefined;\n const v = raw.trim().toLowerCase();\n if (v === '1' || v === 'true') return true;\n if (v === '0' || v === 'false') return false;\n if (v === 'transparent') return 'transparent';\n return undefined;\n}\n/** \u67D0\u4E2A uiAutoHide \u53D6\u503C\u5728\u5F53\u524D\u573A\u666F\u4E0B\u662F\u5426\u751F\u6548 (\u9690\u85CF + \u70B9\u51FB\u51FA\u73B0)\u3002 */\nexport function xcUiAutoHideActive(mode: XcUiAutoHide, sceneTransparent: boolean): boolean {\n return mode === 'transparent' ? sceneTransparent : mode;\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u76F8\u673A (camera) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/**\n * \u5BBF\u4E3B\u53EF\u8C03\u7684\u76F8\u673A\u9009\u9879 (SDK `camera` \u9009\u9879 / `?cameraFov=` \u7B49 URL \u53C2\u6570 / `xc.setConfig{camera}`)\u3002\n * fov \u89C6\u91CE\u89D2 (\u5EA6, \u9ED8\u8BA4 30): \u8D8A\u5C0F\u8D8A\u50CF\u957F\u7126 (\u900F\u89C6\u538B\u7F29\u3001\u80CC\u666F\u865A), \u8D8A\u5927\u8D8A\u5E7F\u89D2\u3002\u4E3B\u4F53\u53D6\u666F\u5927\u5C0F\u4F1A\u968F fov \u81EA\u52A8\u8865\u507F (\u8DDD\u79BB = \u53D6\u666F\u8303\u56F4 / (2\u00B7tan(fov/2))), \u6240\u4EE5\u53EA\u6539 fov \u65F6\u89D2\u8272\u5728\u753B\u9762\u91CC\u7684\u5927\u5C0F\u57FA\u672C\u4E0D\u53D8\n * distance \u76F8\u673A\u5230\u89D2\u8272\u7684\u89C6\u8DDD (\u7C73, \u9ED8\u8BA4\u7EA6 2.5): \u8D8A\u5C0F\u89D2\u8272\u8D8A\u5927\u3002\u663E\u5F0F\u8BBE\u7F6E\u540E, \u9996\u6B21\u53D6\u666F\u65F6\u5FFD\u7565 iframe \u91CC\u4FDD\u5B58\u7684\u89C6\u8DDD / \u4FEF\u4EF0 (\u7528\u6237\u4E4B\u540E\u6EDA\u8F6E\u7F29\u653E\u4ECD\u53EF\u8C03, \u4E14\u6362\u88C5 / \u6362\u573A\u666F\u4E0D\u4F1A\u88AB\u62FD\u56DE)\n * height \u53D6\u666F\u9AD8\u5EA6\u504F\u79FB (\u7C73, \u9ED8\u8BA4 0): \u6B63\u503C = \u76F8\u673A\u8FDE\u540C\u76EE\u6807\u4E00\u8D77\u4E0A\u62AC (\u89D2\u8272\u5728\u753B\u9762\u91CC\u4E0B\u79FB), \u8D1F\u503C\u76F8\u53CD\u3002\u663E\u5F0F\u503C\u4E0D\u5199\u5165 iframe \u7684\u4FDD\u5B58\u503C\n * intro \u662F\u5426\u64AD\u653E\u52A0\u8F7D\u5B8C\u6210\u540E\u7684\u63A8\u955C\u5934\u52A8\u753B (\u9ED8\u8BA4 true); false = \u76F4\u63A5\u653E\u5230\u7EC8\u70B9\n * \u7F3A\u7701 / undefined = \u4E0D\u8986\u76D6 (\u8D70 iframe \u4FDD\u5B58\u503C / \u9ED8\u8BA4\u503C); `xc.setConfig` \u91CC null = \u6E05\u9664\u8BE5\u9879\u8986\u76D6\u3002\n */\nexport interface XcCamera {\n fov?: number | null;\n distance?: number | null;\n height?: number | null;\n intro?: boolean | null;\n}\n/**\n * \u5404\u6570\u503C\u9879\u5141\u8BB8\u7684\u8303\u56F4 (\u8D8A\u754C\u503C\u4F1A\u88AB\u5939\u5230\u8FB9\u754C)\u3002\u4E0E\u4E3B\u4ED3\u5E93 APP_CONFIG.camera (minFov/maxFov\u3001defaultMin/MaxDistance\u3001hostMaxYOffset)\n * \u4E00\u81F4, \u4E3B\u4ED3\u5E93\u6709\u5355\u6D4B\u6838\u5BF9\u3002capabilities.camera \u4E5F\u4F1A\u4E0A\u62A5\u8FD9\u4EFD\u8303\u56F4\u3002\n */\nexport const XC_CAMERA_RANGES = {\n fov: [15, 60],\n distance: [1, 15],\n height: [-1, 1],\n} as const;\nconst XC_CAMERA_NUM_KEYS = ['fov', 'distance', 'height'] as const;\nconst XC_CAMERA_KEYS = ['fov', 'distance', 'height', 'intro'] as const;\n\nexport type XcCameraNormalized =\n | { ok: true; camera: XcCamera; /** \u88AB\u5939\u5230\u8303\u56F4\u8FB9\u754C\u7684\u9879 (\u4F9B\u544A\u8B66) */ clamped: string[] }\n | { ok: false; error: string };\n\n/**\n * \u6821\u9A8C + \u89C4\u6574\u4E00\u4E2A camera \u5BF9\u8C61 (xc.setConfig / SDK \u9009\u9879\u5171\u7528)\u3002\n * \u6570\u5B57\u9879\u5FC5\u987B\u662F\u6709\u9650 number (\u8D8A\u754C\u5939\u5230\u8303\u56F4\u5185); intro \u5FC5\u987B\u662F boolean; null = \u6E05\u9664; \u672A\u77E5\u952E / \u975E\u5BF9\u8C61 \u2192 \u9519\u8BEF\u3002\n */\nexport function normalizeXcCamera(raw: unknown): XcCameraNormalized {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) return { ok: false, error: 'camera must be an object' };\n const src = raw as Record<string, unknown>;\n for (const k of Object.keys(src)) {\n if (!(XC_CAMERA_KEYS as readonly string[]).includes(k)) return { ok: false, error: `unknown camera key: ${k} (expected ${XC_CAMERA_KEYS.join(' | ')})` };\n }\n const out: XcCamera = {};\n const clamped: string[] = [];\n for (const k of XC_CAMERA_NUM_KEYS) {\n const v = src[k];\n if (v === undefined) continue;\n if (v === null) { out[k] = null; continue; }\n if (typeof v !== 'number' || !Number.isFinite(v)) return { ok: false, error: `camera.${k} must be a finite number or null` };\n const [lo, hi] = XC_CAMERA_RANGES[k];\n const c = Math.min(hi, Math.max(lo, v));\n if (c !== v) clamped.push(k);\n out[k] = c;\n }\n if (src.intro !== undefined) {\n if (src.intro !== null && typeof src.intro !== 'boolean') return { ok: false, error: 'camera.intro must be a boolean or null' };\n out.intro = src.intro;\n }\n return { ok: true, camera: out, clamped };\n}\n\n/**\n * \u89E3\u6790 URL \u53C2\u6570 cameraFov / cameraDistance / cameraHeight / cameraIntro (SDK \u751F\u6210 / \u624B\u5199 iframe \u5747\u53EF)\u3002\n * \u6570\u503C\u975E\u6CD5 \u2192 \u5FFD\u7565\u5E76\u8BB0 warning; \u8D8A\u754C \u2192 \u5939\u5230\u8303\u56F4\u5E76\u8BB0 warning; cameraIntro \u8BA4 0|1|true|false\u3002\n */\nexport function parseXcCameraParams(get: (name: string) => string | null): { camera: XcCamera; warnings: string[] } {\n const camera: XcCamera = {};\n const warnings: string[] = [];\n const names = { fov: 'cameraFov', distance: 'cameraDistance', height: 'cameraHeight' } as const;\n for (const k of XC_CAMERA_NUM_KEYS) {\n const raw = get(names[k]);\n if (raw === null) continue;\n const t = raw.trim();\n const n = t === '' ? NaN : Number(t);\n if (!Number.isFinite(n)) { warnings.push(`ignoring invalid ?${names[k]}=${raw}`); continue; }\n const [lo, hi] = XC_CAMERA_RANGES[k];\n const c = Math.min(hi, Math.max(lo, n));\n if (c !== n) warnings.push(`?${names[k]}=${raw} is out of range [${lo}, ${hi}], clamped to ${c}`);\n camera[k] = c;\n }\n const intro = get('cameraIntro');\n if (intro !== null) {\n const v = intro.trim().toLowerCase();\n if (v === '1' || v === 'true') camera.intro = true;\n else if (v === '0' || v === 'false') camera.intro = false;\n else warnings.push(`ignoring invalid ?cameraIntro=${intro}`);\n }\n return { camera, warnings };\n}\n\nexport const XC_ID_RE = /^[a-z][a-z0-9_]{0,63}$/;\nexport const isXcId = (v: unknown): v is string => typeof v === 'string' && XC_ID_RE.test(v);\n/** \u53EA\u8BA4\u5BF9\u8C61\u81EA\u5DF1\u7684\u5C5E\u6027 (\u4E0D\u8D70\u539F\u578B\u94FE); \u7B49\u4EF7\u4E8E Object.hasOwn, \u4F46\u517C\u5BB9 ES2020 \u76EE\u6807\u3002 */\nexport const xcHasOwn = (o: object, k: string): boolean => Object.prototype.hasOwnProperty.call(o, k);\n\n/** \u5185\u7F6E\u573A\u666F id (\u4EE5 xc.ready.capabilities.scenes \u4E3A\u51C6; \u76EE\u524D\u5C31\u8FD9\u4E09\u4E2A\u4E3B\u9898, \u4E0D\u542B\u771F\u5B9E\u573A\u666F\u8D44\u4EA7)\u3002 */\nexport type XcSceneId = 'light' | 'dark' | 'transparent';\nexport type XcLang = 'zh-CN' | 'en' | 'ja';\n/** iframe \u652F\u6301\u7684\u754C\u9762\u8BED\u8A00 (\u4E0E\u4E3B\u7AD9 SUPPORTED_LANGS \u540C\u4E00\u4EFD; capabilities.ui.langs)\u3002 */\nexport const XC_LANGS: readonly XcLang[] = ['zh-CN', 'en', 'ja'];\nexport type XcPhase = 'idle' | 'loading' | 'thinking' | 'speaking' | 'listening' | 'paused';\n/** lazy = \u4E0D\u9884\u70ED WebLLM / EMAGE, \u9996\u6B21\u4E92\u52A8\u518D\u52A0\u8F7D (\u9ED8\u8BA4); eager = \u7ACB\u5373\u9884\u70ED\u3002 */\nexport type XcHeavyMode = 'lazy' | 'eager';\n\nexport interface XcEnvelope<T extends string = string, P = unknown> {\n type: T;\n v: typeof XC_PROTOCOL_VERSION;\n /** \u5BBF\u4E3B\u547D\u4EE4\u53EF\u5E26 id; \u5BF9\u5E94\u7684 xc.error / xc.utterance \u4F1A\u56DE\u5E26\u540C\u4E00\u4E2A id\u3002 */\n id?: string;\n payload: P;\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u5BBF\u4E3B \u2192 iframe payload \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nexport interface XcConfig {\n lang?: XcLang;\n /** \u80CC\u666F\u900F\u660E (\u53E0\u5728\u5BBF\u4E3B\u9875\u9762\u4E0A)\u3002 */\n transparent?: boolean;\n /**\n * \u8981\u663E\u793A\u7684 iframe \u5185\u7F6E\u754C\u9762\u90E8\u4EF6 (\u89C1 XC_UI_PARTS), \u4F8B\u5982 ['outfit','scene']; \u7701\u7565 / \u7A7A\u6570\u7EC4 = \u4E0D\u663E\u793A\u3002\n * \u4F20 true/false \u662F 0.1.14 \u7684\u65E7\u5199\u6CD5 (true = chat + bubble), \u5DF2\u5F03\u7528, \u4EC5\u4E3A\u517C\u5BB9\u4FDD\u7559\u3002\n */\n ui?: XcUiPart[] | boolean;\n /**\n * \u5185\u7F6E\u754C\u9762\u7684\u663E\u793A\u7B56\u7565 (\u89C1 XcUiAutoHide): \u9ED8\u8BA4 'transparent' = \u4E0E Tauri \u4E00\u81F4 (\u53EA\u5728\u900F\u660E\u573A\u666F\u9690\u85CF\u3001\u5355\u51FB\u89D2\u8272\u51FA\u73B0, \u4EAE / \u6697\u573A\u666F\u5E38\u663E); true = \u6240\u6709\u573A\u666F\u90FD\u70B9\u51FB\u51FA\u73B0; false = \u4E00\u76F4\u663E\u793A\u3002\n * \u4EC5\u5F53 capabilities.ui \u5B58\u5728\u65F6\u624D\u6709\u610F\u4E49 (\u65E7\u7248 /embed \u5FFD\u7565, \u5E38\u663E)\u3002\n */\n uiAutoHide?: XcUiAutoHide;\n /** \u91CD\u8D44\u6E90\u52A0\u8F7D\u7B56\u7565, \u89C1 XcHeavyMode\u3002 */\n heavy?: XcHeavyMode;\n /**\n * \u5141\u8BB8 xc.setModel \u52A0\u8F7D\u4EFB\u610F https URL \u7684 .vrm/.vrmaddon/.vrmbase\u3002\u9ED8\u8BA4 false (\u5173\u95ED): \u53EA\u80FD\u7528 capabilities.outfits \u91CC\u7684\u5185\u7F6E\u670D\u88C5\u3002\n * \u7531\u5BBF\u4E3B SDK \u7684 `allowCustomModel` \u9009\u9879\u53D1\u51FA (\u653E\u5728 xc.init \u7684 config \u91CC)\u3002\n */\n allowCustomModel?: boolean;\n /**\n * \u624B\u52BF\u5F00\u5173 (\u5BBF\u4E3B SDK \u7684 `draggable` / `resizable` \u9009\u9879\u53D1\u51FA; \u653E\u5728 xc.init \u7684 config \u91CC, \u4E5F\u53EF\u7528 xc.setConfig \u8FD0\u884C\u65F6\u6539)\u3002\n * \u9ED8\u8BA4\u5168 false: iframe \u91CC**\u4E0D\u8BC6\u522B**\u5BF9\u5E94\u624B\u52BF\u3001\u4E5F\u4E0D\u62E6\u622A\u4EFB\u4F55\u6307\u9488\u4E8B\u4EF6\u3002\u4EC5\u5F53 capabilities.gestures \u5B58\u5728\u65F6\u624D\u6709\u610F\u4E49 (\u65E7\u7248 /embed \u5FFD\u7565)\u3002\n * move = \u5DE6\u952E\u5728\u89D2\u8272\u4E0A\u62D6\u52A8 \u2192 \u53D1 xc.gesture-move, \u7531\u5BBF\u4E3B\u79FB\u52A8 iframe\n * resize = \u5728 iframe \u56DB\u89D2\u70ED\u533A\u6309\u4E0B\u62D6\u52A8 \u2192 \u53D1 xc.gesture-resize, \u7531\u5BBF\u4E3B\u7F29\u653E iframe\n */\n gestures?: { move?: boolean; resize?: boolean };\n /**\n * \u76F8\u673A\u9009\u9879 (\u89C1 XcCamera); \u653E\u5728 xc.init \u7684 config \u91CC\u6216\u7528 xc.setConfig \u8FD0\u884C\u65F6\u6539 (\u7F3A\u7701\u952E = \u4E0D\u53D8, null = \u6062\u590D\u9ED8\u8BA4, \u975E\u6CD5\u503C \u2192 xc.error{bad_request})\u3002\n * \u8FD0\u884C\u65F6\u6539\u4F1A\u7ACB\u523B\u91CD\u65B0\u53D6\u666F\u5E76\u53D6\u6D88\u8FDB\u884C\u4E2D\u7684\u63A8\u955C\u5934\u3002\u4EC5\u5F53 capabilities.camera \u5B58\u5728\u65F6\u624D\u6709\u610F\u4E49 (\u65E7\u7248 /embed \u5FFD\u7565)\u3002\n */\n camera?: XcCamera;\n}\n\nexport interface XcInitPayload {\n /** \u5BBF\u4E3B\u58F0\u660E\u7684\u81EA\u8EAB origin (iframe \u4F1A\u4E0E event.origin \u4EA4\u53C9\u6821\u9A8C)\u3002 */\n hostOrigin: string;\n config?: XcConfig;\n}\n\nexport interface XcSayPayload {\n text: string;\n /**\n * 'speak' (\u9ED8\u8BA4): \u76F4\u63A5 TTS + \u52A8\u4F5C, \u4E0D\u7ECF\u8FC7 LLM\u3002\n * 'chat': \u5F53\u4F5C\u7528\u6237\u8F93\u5165\u8D70 WebLLM / \u81EA\u5B9A\u4E49 provider (\u4F1A\u89E6\u53D1\u5927\u6A21\u578B\u52A0\u8F7D)\u3002\n */\n mode?: 'speak' | 'chat';\n}\n\n/** \u5BBF\u4E3B\u97F3\u9891\u7684\u7F16\u7801: encoded = \u5BB9\u5668\u683C\u5F0F (mp3/wav/ogg/aac/webm\u2026 \u7531\u6D4F\u89C8\u5668 decodeAudioData \u51B3\u5B9A); pcm16 / float32 = \u65E0\u5934\u539F\u59CB PCM (\u5C0F\u7AEF, \u4EA4\u9519)\u3002 */\nexport type XcAudioFormat = 'encoded' | 'pcm16' | 'float32';\n\nexport interface XcAudioOptions {\n /** \u6C14\u6CE1\u91CC\u663E\u793A\u7684\u6587\u5B57 (\u53EF\u9009; \u4E0D\u5F71\u54CD\u97F3\u9891\u4E0E\u52A8\u4F5C)\u3002 */\n text?: string;\n /** \u662F\u5426\u7531 EMAGE \u6839\u636E\u8FD9\u6BB5\u97F3\u9891\u751F\u6210\u5168\u8EAB\u52A8\u4F5C, \u9ED8\u8BA4 true\u3002false = \u53EA\u64AD\u653E\u97F3\u9891 (\u6B64\u65F6\u5B8C\u5168\u4E0D\u52A0\u8F7D EMAGE)\u3002 */\n motion?: boolean;\n /** \u662F\u5426\u9A71\u52A8\u53E3\u578B (\u97F3\u91CF RMS \u2192 'aa'), \u9ED8\u8BA4 true\u3002 */\n lipsync?: boolean;\n /**\n * \u662F\u5426\u628A\u8FD9\u6BB5\u97F3\u9891\u9001\u5230\u626C\u58F0\u5668\u3002\u9ED8\u8BA4 true\u3002\n * false: \u4ECD\u89E3\u7801\u5E76\u751F\u6210\u52A8\u4F5C\u548C\u53E3\u578B, \u589E\u76CA\u4E3A 0\u3002\u5BBF\u4E3B\u9875\u9762\u81EA\u5DF1\u64AD\u653E\u540C\u4E00\u6BB5\u97F3\u9891\u65F6\u7528\u6765\u907F\u514D\u53E0\u58F0\u3002\n */\n audible?: boolean;\n /**\n * \u64AD\u653E\u500D\u901F, \u9ED8\u8BA4 1, \u8303\u56F4 0.25~3, \u8D8A\u754C\u4F1A\u5939\u5230\u8303\u56F4\u5185\u3002\n * \u8FD9\u6BB5\u97F3\u9891\u548C\u52A8\u4F5C\u65F6\u949F\u4E00\u8D77\u53D8\u5FEB\u53D8\u6162\u3002\u6539\u6B63\u5728\u64AD\u7684\u500D\u901F\u7528 xc.transport\u3002\n */\n playbackRate?: number;\n /**\n * \u97F3\u91CF 0~1, \u9ED8\u8BA4 1\u3002audible \u4E3A false \u65F6\u626C\u58F0\u5668\u4ECD\u662F 0, \u8FD9\u4E2A\u503C\u5148\u8BB0\u4E0B\u3002\n * \u6539\u6B63\u5728\u64AD\u7684\u97F3\u91CF\u7528 xc.transport\u3002\n */\n volume?: number;\n}\n\n/**\n * xc.audio \u2014 \u6574\u6BB5\u97F3\u9891: iframe \u5185\u89E3\u7801 \u2192 16 kHz \u2192 EMAGE \u7A97\u53E3\u63A8\u7406 + \u540C\u4E00\u6BB5\u97F3\u9891\u7684\u65F6\u949F + \u53E3\u578B\u3002\n * xc.utterance start \u5728\u7B2C\u4E00\u6BB5\u65F6\u949F\u8D77\u6B65\u65F6\u53D1 (\u601D\u8003\u52A8\u4F5C\u548C\u9996\u7A97\u63A8\u7406\u4E4B\u540E), \u64AD\u5B8C\u56DE end\u3002\n * audible \u7F3A\u7701\u6216 true \u65F6\u8FD9\u6BB5\u97F3\u9891\u4E5F\u9001\u5230\u626C\u58F0\u5668; false \u65F6\u589E\u76CA\u4E3A 0, \u53EA\u7559\u52A8\u4F5C\u548C\u53E3\u578B\u3002\n * \u4FE1\u5C01 `id` \u7528\u4E8E\u5173\u8054 xc.utterance / xc.error\u3002\u5927 ArrayBuffer \u8BF7\u653E\u8FDB postMessage \u7684 transfer \u5217\u8868 (SDK \u9ED8\u8BA4\u8FD9\u4E48\u505A)\u3002\n */\nexport interface XcAudioPayload extends XcAudioOptions {\n /** ArrayBuffer / Blob (structured clone) \u6216 URL \u5B57\u7B26\u4E32 (iframe \u5185 fetch, \u4EC5 https \u6216\u540C\u6E90, \u9700 CORS)\u3002 */\n source: ArrayBuffer | Blob | string;\n /** MIME \u63D0\u793A, \u5982 'audio/mpeg'\u3002\u4EC5\u4F5C\u4FE1\u606F, \u89E3\u7801\u4EE5\u6D4F\u89C8\u5668\u55C5\u63A2\u4E3A\u51C6\u3002 */\n mimeType?: string;\n /** \u9ED8\u8BA4 'encoded'\u3002 */\n format?: XcAudioFormat;\n /** \u4EC5\u539F\u59CB PCM \u9700\u8981 (\u9ED8\u8BA4 16000); \u5BB9\u5668\u683C\u5F0F\u5FFD\u7565 (\u4EE5\u6587\u4EF6\u5185\u7684\u91C7\u6837\u7387\u4E3A\u51C6)\u3002\u8303\u56F4 8000~96000\u3002 */\n sampleRate?: number;\n /** \u539F\u59CB PCM \u7684\u58F0\u9053\u6570 (\u4EA4\u9519), 1 \u6216 2, \u9ED8\u8BA4 1; \u5185\u90E8\u4E0B\u6DF7\u4E3A\u5355\u58F0\u9053\u3002 */\n channels?: 1 | 2;\n}\n\n/**\n * xc.audio.chunk \u2014 \u6D41\u5F0F\u97F3\u9891\u5206\u5757 (\u539F\u59CB PCM)\u3002\u4FE1\u5C01 `id` = \u6D41 id: \u540C\u4E00\u4E2A id \u7684\u7B2C\u4E00\u4E2A chunk \u5F00\u542F\u4E00\u6B21\u8BF4\u8BDD,\n * \u58F0\u97F3\u65F6\u949F\u8D77\u6B65\u65F6\u56DE xc.utterance start,\n * \u4E4B\u540E\u7684 chunk \u8FFD\u52A0; \u4EE5 xc.audio.end \u6536\u5C3E\u3002\u6BCF\u4E2A chunk \u7684 `data` \u5E94\u653E\u8FDB transfer \u5217\u8868\u3002\n * \u9009\u9879 (text/motion/lipsync/audible/playbackRate/volume) \u53EA\u5728\u7B2C\u4E00\u4E2A chunk \u91CC\u751F\u6548; sampleRate/format \u6574\u6761\u6D41\u5FC5\u987B\u4E00\u81F4\u3002\n */\nexport interface XcAudioChunkPayload extends XcAudioOptions {\n data: ArrayBuffer;\n format: Exclude<XcAudioFormat, 'encoded'>;\n /** 8000~96000\u3002 */\n sampleRate: number;\n channels?: 1 | 2;\n}\n\nexport interface XcAudioEndPayload {\n /** true = \u7ACB\u5373\u6253\u65AD (\u4E22\u5F03\u672A\u64AD\u653E\u7684\u97F3\u9891); \u9ED8\u8BA4 false = \u64AD\u5B8C\u5DF2\u6536\u5230\u7684\u97F3\u9891\u540E\u7ED3\u675F\u3002 */\n abort?: boolean;\n}\n\nexport interface XcMotionPayload {\n /** .vrma URL (https:// \u6216\u540C\u6E90\u8DEF\u5F84)\u3002\u4E0E stop \u4E8C\u9009\u4E00\u3002 */\n url?: string;\n /** \u5185\u7F6E\u52A8\u4F5C\u540D, \u76EE\u524D\u4EC5 'thinking'\u3002 */\n name?: 'thinking';\n stop?: boolean;\n loop?: boolean;\n /** \u6DE1\u5165\u6DE1\u51FA\u79D2\u6570, \u8303\u56F4 0.26~3, \u8C03\u5927\u66F4\u67D4\u548C\u4F46\u54CD\u5E94\u66F4\u6162\u3002 */\n fadeDuration?: number;\n /** \u64AD\u653E\u500D\u901F, \u8303\u56F4 0.25~3\u3002 */\n timeScale?: number;\n mask?: 'all' | 'upperBody';\n}\n\nexport interface XcExpressionPayload {\n name: 'neutral' | 'happy' | 'angry' | 'sad' | 'relaxed' | 'surprised';\n}\n\n/** xc.lookAt \u2014 TODO: \u6682\u4E0D\u652F\u6301, \u6536\u5230\u4F1A\u8FD4\u56DE unsupported\u3002 */\nexport interface XcLookAtPayload {\n /** \u5F52\u4E00\u5316 (-1..1), \u76F8\u5BF9 iframe \u4E2D\u5FC3\u3002 */\n x: number;\n y: number;\n}\n\nexport interface XcPointerPayload {\n /** iframe \u5185 client \u5750\u6807 (px)\u3002SDK \u4F1A\u628A\u5BBF\u4E3B pointer \u4E8B\u4EF6\u6362\u7B97\u597D\u518D\u53D1\u3002 */\n x: number;\n y: number;\n}\n\nexport interface XcSetModelPayload {\n /** \u5B8C\u6574 .vrm / .vrmaddon / .vrmbase URL (\u9700 CORS)\u3002**\u9ED8\u8BA4\u5173\u95ED**, \u9700\u8981 xc.init config.allowCustomModel=true, \u5426\u5219\u56DE unsupported\u3002 */\n url?: string;\n /** \u5185\u7F6E\u670D\u88C5 id (capabilities.outfits \u91CC\u7684 id, \u5982 'xiaochun_maid')\u3002\u88F8\u6A21 'base' \u4E0D\u5BF9\u5916\u5F00\u653E (\u56DE unknown_id)\u3002\u65B0\u4EE3\u7801\u8BF7\u7528 xc.setOutfit\u3002 */\n outfit?: string;\n name?: string;\n}\n\n/** xc.setOutfit \u2014 \u6362\u5185\u7F6E\u670D\u88C5 (id \u6765\u81EA xc.ready.capabilities.outfits)\u3002\u4E32\u884C + last-wins, \u89C1 docs/EMBED.md\u3002 */\nexport interface XcSetOutfitPayload {\n id: string;\n}\n\n/**\n * xc.prefetch \u2014 \u9884\u53D6\u670D\u88C5\u8D44\u6E90\u5230 iframe \u7684 IndexedDB (\u4E4B\u540E xc.setOutfit \u76F4\u63A5\u547D\u4E2D\u7F13\u5B58, \u4E0D\u8D70\u7F51\u7EDC)\u3002\u9ED8\u8BA4\u4E0D\u4F1A\u81EA\u52A8\u53D1\u751F\u3002\n * \u5168\u5C40\u4E32\u884C (\u5E76\u53D1 1); \u5F00\u4E86 heavy:'eager' \u65F6\u6392\u5728 EMAGE \u52A0\u8F7D\u4E4B\u540E, \u4E5F\u4E0D\u4F1A\u548C\u8FDB\u884C\u4E2D\u7684\u6362\u88C5\u62A2\u5E26\u5BBD\u3002\n * ids \u7701\u7565 = \u5168\u90E8\u5185\u7F6E\u670D\u88C5, **\u9664\u4E86**\u4F53\u79EF\u5927\u7684\u5A5A\u7EB1 (xiaochun_wedding, 13.9MB); \u663E\u5F0F\u70B9\u540D\u5219\u7167\u505A\u3002\n * \u5E94\u7B54 xc.prefetched (\u5E26\u540C\u4E00\u4E2A\u4FE1\u5C01 id)\u3002\n */\nexport interface XcPrefetchPayload {\n ids?: string[];\n}\n\n/** xc.prefetched \u2014 \u9884\u53D6\u5B8C\u6210 (\u9010\u9879\u6210\u8D25\u4E0D\u5F71\u54CD\u6574\u4F53; failed \u91CC\u7684 id \u53EF\u7A0D\u540E\u91CD\u8BD5)\u3002 */\nexport interface XcPrefetchedPayload {\n downloaded: string[];\n cached: string[];\n failed: string[];\n /** 'save-data' = \u7528\u6237\u5F00\u4E86\u7701\u6D41\u91CF\u6A21\u5F0F, \u6574\u4E2A\u8BF7\u6C42\u88AB\u8DF3\u8FC7\u3002 */\n skipped?: 'save-data';\n}\n\n/** xc.setScene \u2014 \u8FD0\u884C\u65F6\u5207\u573A\u666F (id \u6765\u81EA xc.ready.capabilities.scenes: light / dark / transparent)\u3002 */\nexport interface XcSetScenePayload {\n id: string;\n}\n\nexport interface XcMicPayload {\n enabled: boolean;\n}\n\n/** xc.transport \u2014 \u6539\u5F53\u524D\u5BBF\u4E3B\u97F3\u9891\u7684\u500D\u901F\u6216\u97F3\u91CF, \u4E0D\u91CD\u5F00\u8FD9\u4E00\u6B21\u8BF4\u8BDD\u3002\u7F3A\u7701\u5B57\u6BB5\u4FDD\u6301\u539F\u503C\u3002 */\nexport interface XcTransportPayload {\n /** 0.25~3\u3002\u8D8A\u754C\u5939\u5230\u8303\u56F4\u5185\u3002\u4E0D\u662F\u6570\u5B57\u5219\u5FFD\u7565\u3002 */\n playbackRate?: number;\n /** 0~1\u3002audible:false \u65F6\u626C\u58F0\u5668\u4ECD\u4E3A 0\u3002 */\n volume?: number;\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 iframe \u2192 \u5BBF\u4E3B payload \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nexport interface XcReadyPayload {\n version: string;\n protocol: typeof XC_PROTOCOL_VERSION;\n capabilities: {\n commands: string[];\n unsupported: string[];\n stt: boolean;\n transparent: boolean;\n /** \u5BBF\u4E3B\u97F3\u9891\u80FD\u529B (xc.audio / xc.audio.chunk)\u3002\u65E7\u7248 /embed \u6CA1\u6709\u8FD9\u4E2A\u5B57\u6BB5\u3002 */\n audio?: {\n formats: XcAudioFormat[];\n streaming: boolean;\n maxSeconds: number;\n /** \u500D\u901F\u8303\u56F4\u3002\u65E7\u7248\u6CA1\u6709\u3002 */\n playbackRate?: { min: number; max: number };\n /** \u97F3\u91CF\u8303\u56F4\u3002\u65E7\u7248\u6CA1\u6709\u3002 */\n volume?: { min: number; max: number };\n };\n /** iframe \u5185 `self.crossOriginIsolated` (true = \u53EF\u7528 SharedArrayBuffer / \u591A\u7EBF\u7A0B wasm)\u3002\u65E7\u7248 /embed \u6CA1\u6709\u8FD9\u4E2A\u5B57\u6BB5\u3002\u4EC5\u8BCA\u65AD\u7528\u3002 */\n crossOriginIsolated?: boolean;\n /** \u53EF\u6362\u7684\u5185\u7F6E\u670D\u88C5 (\u4E0D\u542B\u88F8\u6A21)\u3002\u65E7\u7248 /embed \u6CA1\u6709\u8FD9\u4E2A\u5B57\u6BB5 \u2192 \u5BBF\u4E3B\u5E94\u89C6\u4E3A\"\u4E0D\u652F\u6301 xc.setOutfit\"\u3002 */\n outfits?: XcOutfitInfo[];\n /** \u53EF\u5207\u7684\u573A\u666F\u3002\u65E7\u7248 /embed \u6CA1\u6709\u8FD9\u4E2A\u5B57\u6BB5 \u2192 \u89C6\u4E3A\"\u4E0D\u652F\u6301 xc.setScene\"\u3002 */\n scenes?: XcSceneInfo[];\n /** \u652F\u6301 xc.prefetch\u3002\u65E7\u7248 /embed \u6CA1\u6709\u8FD9\u4E2A\u5B57\u6BB5\u3002 */\n prefetch?: boolean;\n /**\n * iframe \u5185\u624B\u52BF (\u62D6\u52A8 / \u89D2\u843D\u7F29\u653E) \u652F\u6301\u60C5\u51B5; \u65E7\u7248 /embed \u6CA1\u6709\u8FD9\u4E2A\u5B57\u6BB5 \u2192 \u5BBF\u4E3B\u89C6\u4E3A\"\u4E0D\u652F\u6301\", draggable / resizable \u65E0\u6548\u3002\n * cornerSize = \u56DB\u89D2\u70ED\u533A\u8FB9\u957F (CSS px, iframe \u89C6\u53E3\u5DE6\u4E0A/\u53F3\u4E0A/\u5DE6\u4E0B/\u53F3\u4E0B\u5404\u4E00\u4E2A\u6B63\u65B9\u5F62), \u5BBF\u4E3B UI \u53EF\u636E\u6B64\u63D0\u793A\u3002\n */\n gestures?: { move: boolean; resize: boolean; cornerSize: number };\n /**\n * \u5185\u7F6E\u754C\u9762\u80FD\u529B; \u65E7\u7248 /embed \u6CA1\u6709\u8FD9\u4E2A\u5B57\u6BB5 \u2192 \u5BBF\u4E3B\u89C6\u4E3A\"\u6CA1\u6709 lang / github \u90E8\u4EF6\u3001\u4E0D\u652F\u6301 uiAutoHide (\u754C\u9762\u5E38\u663E)\"\u3002\n * parts = \u53EF\u7528\u7684\u90E8\u4EF6\u540D; autoHide = \u652F\u6301 uiAutoHide (\u70B9\u51FB\u51FA\u73B0); langs = \u8BED\u8A00\u6309\u94AE / `lang` \u9009\u9879\u53EF\u7528\u7684\u53D6\u503C\u3002\n */\n ui?: { parts: XcUiPart[]; autoHide: boolean; langs: XcLang[] };\n /**\n * \u76F8\u673A\u9009\u9879\u80FD\u529B (\u89C1 XcCamera); \u65E7\u7248 /embed \u6CA1\u6709\u8FD9\u4E2A\u5B57\u6BB5 \u2192 \u5BBF\u4E3B\u89C6\u4E3A\"\u4E0D\u652F\u6301 camera \u9009\u9879\"\u3002\n * fov / distance / height = \u5404\u9879\u5141\u8BB8\u8303\u56F4 [min, max] (\u8D8A\u754C\u4F1A\u88AB\u5939\u5230\u8FB9\u754C); intro = \u652F\u6301\u5173\u95ED\u63A8\u955C\u5934\u3002\n */\n camera?: { fov: [number, number]; distance: [number, number]; height: [number, number]; intro: boolean };\n };\n}\n\nexport interface XcOutfitInfo {\n id: string;\n /** \u5C55\u793A\u540D (\u82F1\u6587, \u6765\u81EA\u4E3B\u4ED3\u5E93\u914D\u7F6E)\u3002 */\n name: string;\n /** \u670D\u88C5\u6587\u4EF6\u5927\u5C0F\u63D0\u793A (MB, \u5341\u8FDB\u5236, \u7EA6\u503C; \u516C\u5171\u7684\u88F8\u6A21 ~5.7MB \u4E0D\u8BA1)\u3002\u4EC5\u4F9B\u5BBF\u4E3B\u81EA\u5DF1\u7684 UI \u53C2\u8003 (\u5185\u7F6E\u6362\u88C5\u83DC\u5355\u4E0D\u663E\u793A\u4F53\u79EF); \u65E7\u7248 /embed \u6CA1\u6709\u8FD9\u4E2A\u5B57\u6BB5\u3002 */\n sizeMB?: number;\n}\n\nexport interface XcSceneInfo {\n id: string;\n /** true = \u80CC\u666F\u900F\u660E (\u53E0\u5728\u5BBF\u4E3B\u9875\u4E0A, \u53EF\u5F00\u7A7F\u900F)\u3002 */\n transparent: boolean;\n}\n\nexport interface XcLoadProgressPayload {\n /**\n * 'model' = \u9996\u6B21\u52A0\u8F7D; 'outfit' = \u8FD0\u884C\u4E2D\u6362\u88C5 (id = \u76EE\u6807\u670D\u88C5 id, \u81EA\u5B9A\u4E49 URL \u65F6\u7701\u7565);\n * 'prefetch' = \u5BBF\u4E3B\u53D1\u4E86 xc.prefetch \u540E\u7684\u540E\u53F0\u4E0B\u8F7D (id = \u6B63\u5728\u9884\u53D6\u7684\u670D\u88C5)\u3002\u5DF2\u6709\u7684\"model \u8FDB\u5EA6\u6761\"\u903B\u8F91\u8BF7\u5148\u5224\u65AD phase\u3002\n */\n phase: 'model' | 'outfit' | 'prefetch';\n /** 0~100 */\n progress: number;\n id?: string;\n}\n\nexport interface XcLoadedPayload {\n model: string;\n}\n\n/**\n * xc.outfit-changed \u2014 \u670D\u88C5\u5DF2\u751F\u6548\u3002\u4E09\u79CD\u6765\u6E90: \u9996\u6B21\u52A0\u8F7D (initial:true)\u3001xc.setOutfit / xc.setModel \u7684\u5E94\u7B54 (\u5E26\u540C\u4E00\u4E2A\u4FE1\u5C01 id)\u3002\n * noop:true = \u76EE\u6807\u5DF2\u7ECF\u662F\u5F53\u524D\u670D\u88C5 (\u6216\u4E0E\u53E6\u4E00\u4E2A\u540C\u76EE\u6807\u8BF7\u6C42\u5408\u5E76), \u6CA1\u6709\u91CD\u65B0\u52A0\u8F7D; SDK \u53EA\u7528\u5B83 resolve \u5BF9\u5E94\u7684 Promise, \u4E0D\u518D\u89E6\u53D1\u4E8B\u4EF6\u3002\n */\nexport interface XcOutfitChangedPayload {\n /** \u5185\u7F6E\u670D\u88C5 id; \u81EA\u5B9A\u4E49 URL \u6A21\u578B\u4E3A null\u3002 */\n id: string | null;\n name: string;\n previous?: string | null;\n initial?: boolean;\n noop?: boolean;\n}\n\n/** xc.scene-changed \u2014 \u573A\u666F\u5DF2\u751F\u6548 (\u9996\u6B21\u63E1\u624B\u540E\u7684\u5F53\u524D\u573A\u666F / xc.setScene \u5E94\u7B54 / xc.setConfig{transparent} \u5F15\u8D77\u7684\u5207\u6362)\u3002 */\nexport interface XcSceneChangedPayload {\n id: string;\n transparent: boolean;\n previous?: string | null;\n initial?: boolean;\n noop?: boolean;\n}\n\n/**\n * xc.lang-changed \u2014 iframe \u5F53\u524D\u754C\u9762\u8BED\u8A00 (\u63E1\u624B\u540E\u4E0A\u62A5\u4E00\u6B21 initial:true; \u4E4B\u540E\u7528\u6237\u70B9\u8BED\u8A00\u6309\u94AE / xc.setConfig{lang} \u5F15\u8D77\u7684\u53D8\u5316)\u3002\n * \u521D\u59CB\u8BED\u8A00\u4F18\u5148\u7EA7: `?lang=` (SDK `lang` \u9009\u9879) > iframe \u81EA\u5DF1 localStorage \u91CC\u8BB0\u4F4F\u7684\u7528\u6237\u9009\u62E9 > \u6D4F\u89C8\u5668\u8BED\u8A00 > zh-CN\u3002\n */\nexport interface XcLangChangedPayload {\n lang: XcLang;\n previous?: XcLang | null;\n initial?: boolean;\n}\n\nexport interface XcStatePayload {\n phase: XcPhase;\n paused: boolean;\n heavy: XcHeavyMode;\n}\n\nexport type XcSttPayload =\n | { kind: 'state'; state: 'idle' | 'loading' | 'listening' | 'recognizing' | 'error' }\n | { kind: 'progress'; percent: number }\n | { kind: 'text'; text: string };\n\nexport interface XcUtterancePayload {\n phase: 'start' | 'end';\n text: string;\n /** 'text' = xc.say (TTS); 'audio' = xc.audio / xc.audio.chunk (\u5BBF\u4E3B\u97F3\u9891, \u6CA1\u6709\u8D70 TTS)\u3002\u65E7\u7248 /embed \u6CA1\u6709\u8FD9\u4E2A\u5B57\u6BB5\u3002 */\n kind?: 'text' | 'audio';\n}\n\nexport interface XcHitRegionPayload {\n hit: boolean;\n x: number;\n y: number;\n}\n\n/** \u624B\u52BF\u9636\u6BB5: start = \u6309\u4E0B\u540E\u786E\u8BA4\u6210\u624B\u52BF; move = \u589E\u91CF; end = \u7ED3\u675F (\u677E\u624B / \u53D6\u6D88 / \u5931\u7126)\u3002 */\nexport type XcGesturePhase = 'start' | 'move' | 'end';\n\n/**\n * xc.gesture-move / xc.gesture-resize \u7684\u516C\u5171\u5B57\u6BB5\u3002\u5355\u4F4D CSS px (\u5C4F\u5E55\u5750\u6807\u5DEE, iframe \u88AB\u79FB\u52A8\u65F6\u4ECD\u7A33\u5B9A)\u3002\n * - gesture: \u624B\u52BF\u5E8F\u53F7, \u6BCF\u6B21\u65B0\u624B\u52BF +1 (\u4ECE 1 \u8D77); \u5BBF\u4E3B\u636E\u6B64\u533A\u5206\u624B\u52BF, \u4E22\u5F03\u8FC7\u671F\u624B\u52BF\u7684\u8FDF\u5230\u6D88\u606F\u3002\n * - seq: \u8BE5\u624B\u52BF\u5185\u7684\u6D88\u606F\u5E8F\u53F7, start = 0, \u4E4B\u540E\u4E25\u683C\u9012\u589E; \u5BBF\u4E3B\u4E22\u5F03 seq \u4E0D\u9012\u589E\u7684\u6D88\u606F\u3002\n * - dx / dy: \u76F8\u5BF9\u4E0A\u4E00\u6761\u6D88\u606F\u7684\u589E\u91CF; totalDx / totalDy: \u76F8\u5BF9 start \u7684\u7D2F\u8BA1 (\u5BBF\u4E3B\u4EE5\u5B83\u4E3A\u51C6\u8BA1\u7B97, \u589E\u91CF\u53EA\u662F\u53C2\u8003, \u4E22\u5305\u4E5F\u4E0D\u6F02\u79FB)\u3002\n * - reason: \u4EC5 phase='end' \u5E26: up = \u677E\u624B, cancel = \u6307\u9488\u88AB\u7CFB\u7EDF\u53D6\u6D88, blur = \u7A97\u53E3\u5931\u7126\u3002\n */\nexport interface XcGestureBase {\n gesture: number;\n seq: number;\n phase: XcGesturePhase;\n dx: number;\n dy: number;\n totalDx: number;\n totalDy: number;\n reason?: 'up' | 'cancel' | 'blur';\n}\nexport type XcGestureMovePayload = XcGestureBase;\nexport type XcGestureCorner = 'NW' | 'NE' | 'SW' | 'SE';\nexport interface XcGestureResizePayload extends XcGestureBase {\n /** \u88AB\u62D6\u7684\u89D2; \u5BF9\u89D2\u56FA\u5B9A\u4E0D\u52A8\u3002 */\n corner: XcGestureCorner;\n}\n\nexport interface XcErrorPayload {\n code: XcErrorCode;\n message: string;\n /** \u89E6\u53D1\u9519\u8BEF\u7684\u547D\u4EE4\u540D (\u5982 'xc.lookAt')\u3002 */\n command?: string;\n}\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u7C7B\u578B\u6620\u5C04 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nexport interface XcHostPayloadMap {\n 'xc.init': XcInitPayload;\n 'xc.say': XcSayPayload;\n 'xc.audio': XcAudioPayload;\n 'xc.audio.chunk': XcAudioChunkPayload;\n 'xc.audio.end': XcAudioEndPayload;\n 'xc.motion': XcMotionPayload;\n 'xc.expression': XcExpressionPayload;\n 'xc.lookAt': XcLookAtPayload;\n 'xc.pointer': XcPointerPayload;\n 'xc.setModel': XcSetModelPayload;\n 'xc.setOutfit': XcSetOutfitPayload;\n 'xc.setScene': XcSetScenePayload;\n 'xc.prefetch': XcPrefetchPayload;\n 'xc.setConfig': XcConfig;\n 'xc.mic': XcMicPayload;\n 'xc.transport': XcTransportPayload;\n 'xc.pause': undefined;\n 'xc.resume': undefined;\n 'xc.destroy': undefined;\n}\n\nexport interface XcFramePayloadMap {\n 'xc.ready': XcReadyPayload;\n 'xc.load.progress': XcLoadProgressPayload;\n 'xc.loaded': XcLoadedPayload;\n 'xc.outfit-changed': XcOutfitChangedPayload;\n 'xc.scene-changed': XcSceneChangedPayload;\n 'xc.lang-changed': XcLangChangedPayload;\n 'xc.prefetched': XcPrefetchedPayload;\n 'xc.state': XcStatePayload;\n 'xc.stt': XcSttPayload;\n 'xc.utterance': XcUtterancePayload;\n 'xc.hit-region': XcHitRegionPayload;\n 'xc.gesture-move': XcGestureMovePayload;\n 'xc.gesture-resize': XcGestureResizePayload;\n 'xc.error': XcErrorPayload;\n}\n\nexport type XcHostMessage = {\n [K in XcHostMessageType]: XcEnvelope<K, XcHostPayloadMap[K]>;\n}[XcHostMessageType];\n\nexport type XcFrameMessage = {\n [K in XcFrameMessageType]: XcEnvelope<K, XcFramePayloadMap[K]>;\n}[XcFrameMessageType];\n\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u5C0F\u5DE5\u5177 (SDK \u4E0E /embed \u5171\u7528) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nexport function xcMessage<K extends XcHostMessageType | XcFrameMessageType>(\n type: K,\n payload?: unknown,\n id?: string,\n): XcEnvelope<K, any> {\n const m: XcEnvelope<K, any> = { type, v: XC_PROTOCOL_VERSION, payload };\n if (id !== undefined) m.id = id;\n return m;\n}\n\n/** \u662F\u5426\u4E3A\u5408\u6CD5 xc \u4FE1\u5C01 (\u53EA\u68C0\u67E5\u7ED3\u6784, \u4E0D\u68C0\u67E5 payload)\u3002 */\nexport function isXcEnvelope(data: unknown): data is XcEnvelope {\n if (!data || typeof data !== 'object') return false;\n const d = data as Record<string, unknown>;\n return typeof d.type === 'string' && d.type.startsWith('xc.') && d.v === XC_PROTOCOL_VERSION;\n}\n\n/**\n * \u628A\u7528\u6237\u7ED9\u7684 origin \u89C4\u6574\u6210\u4E25\u683C\u7684 `scheme://host[:port]`\u3002\n * \u62D2\u7EDD '*' / \u901A\u914D / \u975E http(s) / \u5E26\u8DEF\u5F84\u7684\u5B57\u7B26\u4E32 \u2192 \u8FD4\u56DE null\u3002\n */\nexport function normalizeOrigin(input: string | null | undefined): string | null {\n if (!input || input === '*' || input === 'null') return null;\n try {\n const u = new URL(input);\n if (u.protocol !== 'https:' && u.protocol !== 'http:') return null;\n return u.origin;\n } catch {\n return null;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/*! @firetable/project-xiaochun v0.1.
|
|
1
|
+
/*! @firetable/project-xiaochun v0.1.19 | MIT | https://github.com/FireTable/project-xiaochun */
|
|
2
2
|
import {
|
|
3
3
|
createXiaochun
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WYS3WVG4.js";
|
|
5
5
|
import {
|
|
6
6
|
isXcId,
|
|
7
7
|
parseXcUiAutoHide
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-RTBOYZAZ.js";
|
|
9
9
|
|
|
10
10
|
// src/avatar-element.ts
|
|
11
11
|
var OBSERVED = [
|
|
@@ -218,6 +218,12 @@ function createElementClass() {
|
|
|
218
218
|
speakAudioStream(opts) {
|
|
219
219
|
return this.requireClient().speakAudioStream(opts);
|
|
220
220
|
}
|
|
221
|
+
setPlaybackRate(rate) {
|
|
222
|
+
this.client?.setPlaybackRate(rate);
|
|
223
|
+
}
|
|
224
|
+
setVolume(volume) {
|
|
225
|
+
this.client?.setVolume(volume);
|
|
226
|
+
}
|
|
221
227
|
motion(m) {
|
|
222
228
|
return this.requireClient().motion(m);
|
|
223
229
|
}
|
|
@@ -267,4 +273,4 @@ export {
|
|
|
267
273
|
XIAOCHUN_ELEMENT_TAG,
|
|
268
274
|
defineXiaochunElement
|
|
269
275
|
};
|
|
270
|
-
//# sourceMappingURL=chunk-
|
|
276
|
+
//# sourceMappingURL=chunk-SPCT7EQA.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/avatar-element.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * avatar-element.ts \u2014 `<xiaochun-avatar>` Web Component (Shadow DOM \u5185\u5305 iframe)\u3002\n *\n * \u5C5E\u6027: src outfit scene model(deprecated) lang mic transparent draggable resizable min-size max-size border-radius position size lazy paused placeholder heavy ui ui-autohide controls allowed-origins cross-origin-isolated persist allow-custom-model\n * - \u5E03\u5C14\u5C5E\u6027: \u7F3A\u7701\u53D6\u9ED8\u8BA4\u503C; \"\" / \"true\" = true; \"false\" = false\u3002\n * - size=\"320x480\" \u6216 size=\"320\" (\u9AD8 = \u5BBD \u00D7 1.5); \u4E5F\u53EF\u5199 CSS \u957F\u5EA6 \"100%x480px\"\u3002\u7F3A\u7701 = 600x1080 (\u5BBD\u4E0D\u8D85\u8FC7\u5BB9\u5668\u3001\u9AD8\u4E0D\u8D85\u8FC7\u89C6\u53E3)\u3002\u6539 size **\u70ED\u66F4\u65B0** (setSize, \u4E0D\u91CD\u5EFA iframe)\u3002\n * - draggable: \u624B\u52BF\u62D6\u52A8 (\u89D2\u8272\u4E0A\u6309\u4F4F\u62D6, \u5185\u8054 / \u60AC\u6D6E\u90FD\u884C); resizable: \u56DB\u89D2\u7F29\u653E\u70ED\u533A (\u5BF9\u89D2\u56FA\u5B9A, \u4E0D\u91CD\u5EFA iframe); \u4E24\u8005\u9ED8\u8BA4\u5173, \u6539\u5C5E\u6027\u70ED\u66F4\u65B0\u3002\n * min-size / max-size=\"120x180\": resizable \u7684\u6700\u5C0F / \u6700\u5927\u5C3A\u5BF8 (px, \u7F3A\u7701\u6700\u5C0F 120x180\u3001\u6700\u5927\u53EA\u53D7\u89C6\u53E3\u9650\u5236)\u3002\n * - border-radius: \u5916\u58F3\u5706\u89D2 (\u6570\u5B57 = px \u6216 CSS \u957F\u5EA6); \u7F3A\u7701: \u975E\u900F\u660E\u573A\u666F 20px (\u4E0E Tauri \u684C\u5BA0\u7A97\u53E3\u540C\u503C) / \u900F\u660E 0; \u6539\u5C5E\u6027\u70ED\u66F4\u65B0 (setBorderRadius)\u3002\n * - outfit: \u5185\u7F6E\u670D\u88C5 id (xiaochun_maid); \u6539\u5C5E\u6027**\u70ED\u66F4\u65B0** (setOutfit, \u4E0D\u91CD\u5EFA iframe)\u3002model \u662F outfit \u7684\u65E7\u522B\u540D (deprecated)\u3002\n * - scene: light | dark | transparent; \u6539\u5C5E\u6027**\u70ED\u66F4\u65B0** (setScene, \u4E0D\u91CD\u5EFA iframe)\u3002\n * - ui: \u8981\u663E\u793A\u7684 iframe \u5185\u7F6E\u754C\u9762\u90E8\u4EF6, \u9017\u53F7\u5206\u9694: chat,bubble,outfit,scene,lang,github (\u4F8B ui=\"outfit,scene,lang,github\"); \u7F3A\u7701 = \u90FD\u4E0D\u663E\u793A; \u6539\u5B83\u4F1A\u91CD\u5EFA\u3002\n * \u65E7\u5199\u6CD5 ui / ui=\"true\" \u5DF2\u5F03\u7528 (= chat,bubble, \u4F1A console.warn)\u3002\n * - ui-autohide: \u5185\u7F6E\u754C\u9762\u7684\u663E\u793A\u7B56\u7565: \u7F3A\u7701 / \"transparent\" = \u4E0E Tauri \u4E00\u81F4 (\u53EA\u5728\u900F\u660E\u573A\u666F\u70B9\u51FB\u51FA\u73B0, \u4EAE\u6697\u573A\u666F\u5E38\u663E); \"true\" = \u6240\u6709\u573A\u666F\u70B9\u51FB\u51FA\u73B0; \"false\" = \u4E00\u76F4\u663E\u793A\u3002\u6539\u5B83\u4F1A\u91CD\u5EFA\u3002\n * - persist-box: \u8BB0\u4F4F\u7528\u6237\u62D6\u52A8 / \u7F29\u653E\u540E\u7684\u4F4D\u7F6E\u548C\u5927\u5C0F (\u5BBF\u4E3B localStorage): \"\" / \"true\" = \u9ED8\u8BA4 key, \u5176\u5B83\u5B57\u7B26\u4E32 = \u547D\u540D\u7A7A\u95F4 (xiaochun:box:<\u540D\u5B57>); \u9700\u914D\u5408 draggable / resizable; \u663E\u5F0F size \u4F18\u5148\u4E8E\u5DF2\u4FDD\u5B58\u7684\u5927\u5C0F\u3002\u6539\u5B83\u4F1A\u91CD\u5EFA\u3002\n * - persist: \"host\" \u6216\u81EA\u5B9A\u4E49 localStorage key; \u53E6\u628A\u670D\u88C5/\u573A\u666F\u504F\u597D\u5B58\u5728\u5BBF\u4E3B\u9875 (\u53EF\u9009; \u9ED8\u8BA4\u4E0D\u5B58, iframe \u81EA\u5DF1\u7684 localStorage \u4ECD\u4F1A\u8BB0\u4F4F)\u3002\n * - prefetch: \"\" / \"true\" = \u9884\u53D6\u5168\u90E8\u5185\u7F6E\u670D\u88C5 (\u5A5A\u7EB1\u9664\u5916), \u6216\u9017\u53F7\u5206\u9694\u7684 id \u5217\u8868; \u53EA\u5728 heavy=\"eager\" \u65F6\u81EA\u52A8\u89E6\u53D1 (\u9ED8\u8BA4\u5173)\u3002\n * - allow-custom-model: \u5141\u8BB8 setModel({url}) \u52A0\u8F7D\u4EFB\u610F https \u6A21\u578B (\u9ED8\u8BA4\u5173\u95ED)\u3002\n * \u6837\u5F0F (CSS \u81EA\u5B9A\u4E49\u5C5E\u6027, \u53EF\u5199\u5728 <xiaochun-avatar> \u4E0A\u6216\u4EFB\u610F\u7956\u5148\u4E0A; \u53D6\u503C\u8303\u56F4/\u6548\u679C\u89C1 client.ts \u91CC\u7684\u6CE8\u91CA\u4E0E docs/EMBED.md \u00A7\u6837\u5F0F):\n * --xc-radius --xc-shadow --xc-z-index --xc-offset-x --xc-offset-y --xc-bg\n * \u53EF\u7528 ::part() \u5B9A\u5236\u5916\u58F3: ::part(mount) ::part(wrapper) ::part(iframe) ::part(placeholder) (iframe \u5185\u90E8\u4E0D\u53EF\u88AB\u5BBF\u4E3B CSS \u5F71\u54CD)\n * \u4E8B\u4EF6 (CustomEvent, composed, detail = \u534F\u8BAE payload): xc-ready(\u6A21\u578B\u52A0\u8F7D\u5B8C) xc-progress xc-state xc-stt xc-utterance xc-error xc-outfit-changed xc-scene-changed xc-lang-changed\n * xc-move / xc-resize (\u7528\u6237\u62D6\u52A8 / \u7F29\u653E, detail = {phase, left, top, width, height})\n * \u65B9\u6CD5: say(text) speakAudio(source, opts) speakAudioStream(opts) setPlaybackRate(rate) setVolume(volume) motion(m) expression(name) setOutfit(id) setScene(id) getOutfits() getScenes() destroy() (+ client \u5C5E\u6027\u62FF\u5230\u5B8C\u6574 SDK \u5B9E\u4F8B)\n */\nimport {\n createXiaochun,\n type XiaochunAudioOptions,\n type XiaochunAudioSource,\n type XiaochunAudioStream,\n type XiaochunInstance,\n type XiaochunLazy,\n type XiaochunPosition,\n type XiaochunResizeLimits,\n} from './client';\nimport { isXcId, parseXcUiAutoHide } from './protocol';\nimport type { XcCamera, XcUiPart, XcExpressionPayload, XcHeavyMode, XcLang, XcMotionPayload, XcOutfitInfo, XcPrefetchedPayload, XcSceneInfo } from './protocol';\n\nconst OBSERVED = [\n 'src', 'outfit', 'scene', 'model', 'lang', 'mic', 'transparent', 'draggable', 'resizable', 'min-size', 'max-size', 'border-radius', 'position', 'size', 'lazy',\n 'paused', 'placeholder', 'heavy', 'ui', 'ui-autohide', 'camera-fov', 'camera-distance', 'camera-height', 'camera-intro', 'controls', 'allowed-origins', 'cross-origin-isolated', 'persist', 'persist-box', 'allow-custom-model', 'prefetch',\n] as const;\n/** \u6539\u4E86\u8FD9\u4E9B\u8981\u91CD\u5EFA iframe; \u5176\u4F59 (outfit / scene / model / lang / mic / paused / size / draggable / resizable / min-size / max-size / border-radius / camera-*) \u53EF\u4EE5\u70ED\u66F4\u65B0\u3002 */\nconst REBUILD = new Set(['src', 'position', 'lazy', 'placeholder', 'heavy', 'ui', 'ui-autohide', 'controls', 'allowed-origins', 'transparent', 'cross-origin-isolated', 'persist', 'persist-box', 'allow-custom-model', 'prefetch']);\n\n/** border-radius \u5C5E\u6027: \u7EAF\u6570\u5B57 = px, \u5176\u4F59\u5F53 CSS \u957F\u5EA6; \u7F3A\u7701 = undefined (\u8D70 SDK \u9ED8\u8BA4: \u975E\u900F\u660E 20px / \u900F\u660E 0)\u3002 */\nfunction radiusAttr(v: string | null): number | string | undefined {\n if (v === null || v.trim() === '') return undefined;\n const t = v.trim();\n return /^\\d+(\\.\\d+)?$/.test(t) ? Number(t) : t;\n}\n\nfunction boolAttr(el: Element, name: string, dflt: boolean): boolean {\n const v = el.getAttribute(name);\n if (v === null) return dflt;\n return v !== 'false';\n}\n\n/** prefetch \u5C5E\u6027: \u7F3A\u7701/false = \u5173; \"\" / \"true\" = \u5168\u90E8 (\u5A5A\u7EB1\u9664\u5916); \u5176\u5B83 = \u9017\u53F7\u5206\u9694\u7684 id \u5217\u8868\u3002 */\nfunction prefetchAttr(v: string | null): boolean | string[] {\n if (v === null || v === 'false') return false;\n if (v === '' || v === 'true') return true;\n return v.split(',').map((s) => s.trim()).filter(Boolean);\n}\n\n/** persist-box \u5C5E\u6027: \"\" / \"true\" \u2192 true; \"false\" / \u7F3A\u7701 \u2192 \u4E0D\u8BB0; \u5176\u5B83\u5B57\u7B26\u4E32 = \u547D\u540D\u7A7A\u95F4\u3002 */\nfunction persistBoxAttr(v: string | null): boolean | string | undefined {\n if (v === null || v === 'false' || v === '0') return undefined;\n if (v === '' || v === 'true' || v === '1') return true;\n return v;\n}\n\n/** ui \u5C5E\u6027: \u7F3A\u7701 / \"false\" / \"0\" = \u4E0D\u663E\u793A; \u9017\u53F7\u5206\u9694\u7684\u90E8\u4EF6\u540D (chat,bubble,outfit,scene,lang,github); \"\" / \"true\" / \"1\" = \u5F03\u7528\u7684\u65E7\u5199\u6CD5 (client \u91CC warn)\u3002 */\nfunction uiAttr(v: string | null): XcUiPart[] | boolean | undefined {\n if (v === null || v === 'false' || v === '0') return undefined;\n if (v === '' || v === 'true' || v === '1') return true;\n return v.split(',').map((s) => s.trim()).filter(Boolean) as XcUiPart[];\n}\n\n/** camera-fov / camera-distance / camera-height / camera-intro \u5C5E\u6027 \u2192 XcCamera; \u6CA1\u5199 / \u975E\u6CD5\u7684\u9879 = null (\u521B\u5EFA\u65F6\u5FFD\u7565, \u70ED\u66F4\u65B0\u65F6 = \u6062\u590D\u9ED8\u8BA4)\u3002 */\nfunction cameraAttrs(el: Element): XcCamera {\n const num = (name: string): number | null => {\n const v = el.getAttribute(name);\n if (v === null || v.trim() === '') return null;\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n };\n const i = el.getAttribute('camera-intro');\n return { fov: num('camera-fov'), distance: num('camera-distance'), height: num('camera-height'), intro: i === null ? null : i !== 'false' && i !== '0' };\n}\n\n/** \"120x180\" \u2192 {w,h} (px \u6570\u5B57); \u975E\u6CD5 \u2192 undefined\u3002 */\nfunction parsePx(v: string | null): { w: number; h: number } | undefined {\n const m = v?.trim().match(/^(\\d+(?:\\.\\d+)?)\\s*x\\s*(\\d+(?:\\.\\d+)?)$/i);\n return m ? { w: Number(m[1]), h: Number(m[2]) } : undefined;\n}\n\n/** size \u5C5E\u6027 \u2192 \u5BBD\u9AD8; \u6CA1\u5199\u7684\u7EF4\u5EA6\u8FD4\u56DE undefined = \u7528 SDK \u9ED8\u8BA4 (600x1080, \u53D7\u89C6\u53E3\u9650\u5236)\u3002 */\nfunction parseSize(v: string | null): { width: string | undefined; height: string | undefined } {\n if (!v) return { width: undefined, height: undefined };\n const m = v.trim().split(/x/i);\n const px = (s: string) => (/^\\d+(\\.\\d+)?$/.test(s) ? `${s}px` : s);\n if (m.length >= 2) return { width: px(m[0]), height: px(m[1]) };\n const w = parseFloat(m[0]);\n return Number.isFinite(w) && /^\\d+(\\.\\d+)?$/.test(m[0]) ? { width: `${w}px`, height: `${Math.round(w * 1.5)}px` } : { width: m[0], height: undefined };\n}\n\n/** \u516C\u5F00\u7C7B\u578B (\u7C7B\u672C\u8EAB\u5728\u6D4F\u89C8\u5668\u91CC\u624D\u521B\u5EFA, \u8FD9\u6837 SSR / Node \u4E0B import \u672C\u5305\u4E0D\u4F1A\u56E0 HTMLElement \u7F3A\u5931\u800C\u629B\u9519)\u3002 */\nexport interface XiaochunAvatarElement extends HTMLElement {\n client: XiaochunInstance | null;\n say(text: string, opts?: { mode?: 'speak' | 'chat' }): Promise<void>;\n /** \u76F4\u63A5\u64AD\u653E\u5BBF\u4E3B\u7ED9\u7684\u97F3\u9891 (\u4E0D\u8D70 TTS), \u540C\u65F6\u7531 EMAGE \u751F\u6210\u52A8\u4F5C + \u53E3\u578B\u3002 */\n speakAudio(source: XiaochunAudioSource, opts?: XiaochunAudioOptions): Promise<void>;\n speakAudioStream(opts: XiaochunAudioOptions & { sampleRate: number }): XiaochunAudioStream;\n setPlaybackRate(rate: number): void;\n setVolume(volume: number): void;\n motion(m: XcMotionPayload | string): Promise<void>;\n expression(name: XcExpressionPayload['name']): Promise<void>;\n /** \u6362\u5185\u7F6E\u670D\u88C5 (\u4E32\u884C + last-wins, \u89C1 XiaochunInstance.setOutfit)\u3002 */\n setOutfit(id: string): Promise<void>;\n setScene(id: string): Promise<void>;\n getOutfits(): Promise<XcOutfitInfo[]>;\n getScenes(): Promise<XcSceneInfo[]>;\n /** \u6E05\u9664 persist-box \u4FDD\u5B58\u7684\u4F4D\u7F6E / \u5927\u5C0F; { reset: true } \u540C\u65F6\u8FD8\u539F\u5230\u521D\u59CB\u4F4D\u7F6E\u548C\u5927\u5C0F\u3002 */\n clearPersistedBox(opts?: { reset?: boolean }): void;\n /** \u9884\u53D6\u670D\u88C5\u8D44\u6E90\u5230 iframe \u7684 IndexedDB; ids \u7701\u7565 = \u5168\u90E8 (\u5A5A\u7EB1\u9664\u5916)\u3002 */\n prefetch(ids?: string[]): Promise<XcPrefetchedPayload>;\n destroy(): void;\n}\n\nfunction createElementClass(): CustomElementConstructor {\n class XiaochunAvatarElementImpl extends HTMLElement {\n static get observedAttributes() { return [...OBSERVED]; }\n\n /** \u5E95\u5C42 SDK \u5B9E\u4F8B (\u8FDE\u63A5 DOM \u540E\u53EF\u7528)\u3002 */\n client: XiaochunInstance | null = null;\n private mountEl: HTMLDivElement | null = null;\n private rebuildScheduled = false;\n\n connectedCallback(): void {\n if (!this.shadowRoot) {\n const root = this.attachShadow({ mode: 'open' });\n // :host \u9ED8\u8BA4 inline-block + max-width:100% (\u914D\u5408 SDK \u7684\u56FA\u5B9A\u5BBD\u9AD8, \u4E0D\u4F1A\u6491\u7834\u7A84\u5C4F\u5BB9\u5668)\u3002\n // \u60AC\u6D6E (position=bottom-right|bottom-left) \u65F6 SDK \u5185\u90E8\u7528 position:fixed, \u4E0D\u53D7 :host \u76D2\u6A21\u578B\u5F71\u54CD\u3002\n root.innerHTML = '<style>:host{display:inline-block;max-width:100%}:host([hidden]){display:none}</style><div part=\"mount\"></div>';\n }\n this.mountEl = this.shadowRoot!.querySelector('div');\n this.build();\n }\n\n disconnectedCallback(): void {\n this.destroy();\n }\n\n attributeChangedCallback(name: string, oldV: string | null, newV: string | null): void {\n if (!this.client || oldV === newV) return;\n if (REBUILD.has(name)) {\n if (!this.rebuildScheduled) {\n this.rebuildScheduled = true;\n queueMicrotask(() => { this.rebuildScheduled = false; if (this.isConnected) { this.destroy(); this.build(); } });\n }\n return;\n }\n const c = this.client;\n // \u70ED\u66F4\u65B0: \u5931\u8D25 (busy / unknown_id / unsupported) \u90FD\u4F1A\u4F5C\u4E3A xc-error \u4E8B\u4EF6\u629B\u7ED9\u5BBF\u4E3B, \u8FD9\u91CC\u4E0D\u91CD\u590D\u5904\u7406\n if ((name === 'outfit' || name === 'model') && newV) {\n // outfit \u4E0E model(\u65E7\u522B\u540D) \u540C\u65F6\u5B58\u5728\u65F6 outfit \u4F18\u5148\n const want = name === 'model' && this.hasAttribute('outfit') ? null : newV;\n if (want) void (isXcId(want) ? c.setOutfit(want) : c.setModel(want)).catch(() => {}); // \u65E7\u7528\u6CD5 model=\"https://\u2026\" \u4ECD\u8D70 setModel\n }\n else if (name === 'scene' && newV) void c.setScene(newV).catch(() => {});\n else if (name === 'lang' && newV) void c.setConfig({ lang: newV as XcLang }).catch(() => {});\n else if (name === 'size') { const { width, height } = parseSize(newV); c.setSize(width, height); }\n else if (name === 'border-radius') c.setBorderRadius(radiusAttr(newV));\n else if (name === 'draggable') c.setDraggable(boolAttr(this, 'draggable', false));\n else if (name === 'resizable' || name === 'min-size' || name === 'max-size') c.setResizable(this.resizableOption());\n else if (name.startsWith('camera-')) void c.setConfig({ camera: cameraAttrs(this) }).catch(() => {});\n else if (name === 'mic') void c.mic(boolAttr(this, 'mic', false)).catch(() => {});\n else if (name === 'paused') { if (boolAttr(this, 'paused', false)) c.pause(); else c.resume(); }\n }\n\n private build(): void {\n if (!this.mountEl || this.client) return;\n const { width, height } = parseSize(this.getAttribute('size'));\n const placeholder = this.getAttribute('placeholder');\n const allowed = this.getAttribute('allowed-origins');\n const lazyAttr = this.getAttribute('lazy');\n const lazy: XiaochunLazy = lazyAttr === 'click' ? 'click' : lazyAttr === 'false' ? false : true;\n const c = createXiaochun({\n container: this.mountEl,\n src: this.getAttribute('src') || undefined,\n allowedOrigins: allowed ? allowed.split(',').map((s) => s.trim()).filter(Boolean) : undefined,\n lazy,\n placeholder: placeholder === 'none' ? false : placeholder || undefined,\n transparent: boolAttr(this, 'transparent', true), // \u60AC\u6D6E\u5934\u50CF\u9ED8\u8BA4\u900F\u660E\n width, height,\n position: (this.getAttribute('position') as XiaochunPosition | null) ?? 'inline',\n draggable: boolAttr(this, 'draggable', false),\n resizable: this.resizableOption(),\n borderRadius: radiusAttr(this.getAttribute('border-radius')),\n lang: (this.getAttribute('lang') as XcLang | null) ?? undefined,\n outfit: this.getAttribute('outfit') ?? this.getAttribute('model') ?? undefined,\n scene: this.getAttribute('scene') ?? undefined,\n persist: this.getAttribute('persist') || undefined,\n persistBox: persistBoxAttr(this.getAttribute('persist-box')),\n allowCustomModel: boolAttr(this, 'allow-custom-model', false),\n prefetch: prefetchAttr(this.getAttribute('prefetch')),\n heavy: (this.getAttribute('heavy') as XcHeavyMode | null) ?? undefined,\n ui: uiAttr(this.getAttribute('ui')),\n uiAutoHide: parseXcUiAutoHide(this.getAttribute('ui-autohide')),\n camera: cameraAttrs(this),\n controls: boolAttr(this, 'controls', true),\n crossOriginIsolated: boolAttr(this, 'cross-origin-isolated', false),\n });\n this.client = c;\n const fwd = (ev: string, name: string) => c.on(ev as 'ready', (detail) =>\n this.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true })));\n fwd('ready', 'xc-ready');\n fwd('progress', 'xc-progress');\n fwd('state', 'xc-state');\n fwd('stt', 'xc-stt');\n fwd('utterance', 'xc-utterance');\n fwd('error', 'xc-error');\n fwd('outfit-changed', 'xc-outfit-changed');\n fwd('scene-changed', 'xc-scene-changed');\n fwd('lang-changed', 'xc-lang-changed');\n fwd('move', 'xc-move');\n fwd('resize', 'xc-resize');\n if (boolAttr(this, 'paused', false)) c.pause();\n if (boolAttr(this, 'mic', false)) void c.ready.then(() => c.mic(true)).catch(() => {});\n }\n\n /** resizable / min-size / max-size \u5C5E\u6027 \u2192 SDK \u7684 resizable \u9009\u9879 (\u7F3A\u7701 / \"false\" = \u5173)\u3002 */\n private resizableOption(): false | XiaochunResizeLimits {\n if (!boolAttr(this, 'resizable', false)) return false;\n const min = parsePx(this.getAttribute('min-size')), max = parsePx(this.getAttribute('max-size'));\n return { minWidth: min?.w, minHeight: min?.h, maxWidth: max?.w, maxHeight: max?.h };\n }\n\n say(text: string, opts?: { mode?: 'speak' | 'chat' }): Promise<void> {\n return this.requireClient().say(text, opts);\n }\n speakAudio(source: XiaochunAudioSource, opts?: XiaochunAudioOptions): Promise<void> {\n return this.requireClient().speakAudio(source, opts);\n }\n speakAudioStream(opts: XiaochunAudioOptions & { sampleRate: number }): XiaochunAudioStream {\n return this.requireClient().speakAudioStream(opts);\n }\n setPlaybackRate(rate: number): void { this.client?.setPlaybackRate(rate); }\n setVolume(volume: number): void { this.client?.setVolume(volume); }\n motion(m: XcMotionPayload | string): Promise<void> { return this.requireClient().motion(m); }\n expression(name: XcExpressionPayload['name']): Promise<void> { return this.requireClient().expression(name); }\n setOutfit(id: string): Promise<void> { return this.requireClient().setOutfit(id); }\n setScene(id: string): Promise<void> { return this.requireClient().setScene(id); }\n getOutfits(): Promise<XcOutfitInfo[]> { return this.requireClient().getOutfits(); }\n getScenes(): Promise<XcSceneInfo[]> { return this.requireClient().getScenes(); }\n clearPersistedBox(opts?: { reset?: boolean }): void { this.client?.clearPersistedBox(opts); }\n prefetch(ids?: string[]): Promise<XcPrefetchedPayload> { return this.requireClient().prefetch(ids); }\n\n destroy(): void {\n this.client?.destroy();\n this.client = null;\n }\n\n private requireClient(): XiaochunInstance {\n if (!this.client) throw new Error('<xiaochun-avatar> is not connected');\n return this.client;\n }\n }\n\n return XiaochunAvatarElementImpl;\n}\n\nexport const XIAOCHUN_ELEMENT_TAG = 'xiaochun-avatar';\n\n/** \u6CE8\u518C\u81EA\u5B9A\u4E49\u5143\u7D20 (\u5E42\u7B49)\u3002import '@firetable/project-xiaochun/element' \u4F1A\u81EA\u52A8\u8C03\u7528\u3002 */\nexport function defineXiaochunElement(tag: string = XIAOCHUN_ELEMENT_TAG): CustomElementConstructor | null {\n if (typeof customElements === 'undefined' || typeof HTMLElement === 'undefined') return null;\n const existing = customElements.get(tag);\n if (existing) return existing;\n const ctor = createElementClass();\n customElements.define(tag, ctor);\n return ctor;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @firetable/project-xiaochun v0.1.
|
|
1
|
+
/*! @firetable/project-xiaochun v0.1.19 | MIT | https://github.com/FireTable/project-xiaochun */
|
|
2
2
|
import {
|
|
3
3
|
XC_CAMERA_RANGES,
|
|
4
4
|
XC_DEFAULT_SRC,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
normalizeXcUiOption,
|
|
12
12
|
parseXcUiAutoHide,
|
|
13
13
|
xcMessage
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-RTBOYZAZ.js";
|
|
15
15
|
|
|
16
16
|
// src/gesture-box.ts
|
|
17
17
|
var DEFAULT_RESIZE_LIMITS = Object.freeze({
|
|
@@ -384,6 +384,9 @@ function createXiaochun(options) {
|
|
|
384
384
|
body.text = rest.text;
|
|
385
385
|
body.motion = rest.motion;
|
|
386
386
|
body.lipsync = rest.lipsync;
|
|
387
|
+
body.audible = rest.audible;
|
|
388
|
+
body.playbackRate = rest.playbackRate;
|
|
389
|
+
body.volume = rest.volume;
|
|
387
390
|
done = send("xc.audio.chunk", body, true, [buf], id);
|
|
388
391
|
bindAbort(signal, id);
|
|
389
392
|
} else {
|
|
@@ -1029,6 +1032,14 @@ function createXiaochun(options) {
|
|
|
1029
1032
|
userPaused = false;
|
|
1030
1033
|
syncPause();
|
|
1031
1034
|
},
|
|
1035
|
+
setPlaybackRate(rate) {
|
|
1036
|
+
if (destroyed || typeof rate !== "number" || !Number.isFinite(rate)) return;
|
|
1037
|
+
postToFrame("xc.transport", { playbackRate: rate });
|
|
1038
|
+
},
|
|
1039
|
+
setVolume(volume) {
|
|
1040
|
+
if (destroyed || typeof volume !== "number" || !Number.isFinite(volume)) return;
|
|
1041
|
+
postToFrame("xc.transport", { volume });
|
|
1042
|
+
},
|
|
1032
1043
|
destroy() {
|
|
1033
1044
|
if (destroyed) return;
|
|
1034
1045
|
try {
|
|
@@ -1080,4 +1091,4 @@ export {
|
|
|
1080
1091
|
defaultHeightCss,
|
|
1081
1092
|
createXiaochun
|
|
1082
1093
|
};
|
|
1083
|
-
//# sourceMappingURL=chunk-
|
|
1094
|
+
//# sourceMappingURL=chunk-WYS3WVG4.js.map
|