@firetable/project-xiaochun 0.1.18 → 0.1.20

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 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,18 +152,21 @@ 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 }); // 流式:比如 TTS 服务边合成边返回的 PCM 块
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()` 只暂停渲染,语音和动作时钟继续走。
163
166
  * 宿主音频的 `utterance start` 在思考动作和 EMAGE 首窗之后、声音时钟起步时才发。页面自己的播放器要等这个事件再出声,口型才对齐。
164
167
  * 失败时 reject:`bad_request`(无法解码 / 空音频 / URL 或 sampleRate 不合法)、`unsupported`、`failed`。
165
168
  * 浏览器仍要求宿主页先有用户手势才能出声,iframe 也要有 `allow="autoplay"`(SDK 已自动设置)。
166
- * 新的 `say()` / `speakAudio()` 会打断正在进行的那一次,被打断的 Promise 会 resolve。
169
+ * 新的 `say()` / `speakAudio()` 会把正在念的一句淡出(大约 0.4 秒;增益已经是 0 则不等),再念新的一句。被打断的 Promise 会 resolve。`stop()`、`AbortSignal`,或 `xc.audio.end` 带 `{ abort: true }`,会立刻切断。`say()` 没有字数上限,长文在 iframe 里按句切开再合成。
167
170
  * `<xiaochun-avatar>` 与 React 的 ref 提供同名的 `speakAudio` / `speakAudioStream` 方法。
168
171
 
169
172
  ---
@@ -175,7 +178,7 @@ const xc = createXiaochun({ container: '#avatar', outfit: 'xiaochun_maid', scene
175
178
  await xc.ready;
176
179
 
177
180
  const outfits = await xc.getOutfits(); // [{ id, name }](裸模永远不会出现在列表里)
178
- const scenes = await xc.getScenes(); // [{ id: 'light' | 'dark' | 'transparent', transparent }]
181
+ const scenes = await xc.getScenes(); // [{ id: 'light' | 'dark' | 'transparent' | 'beach3d', transparent }]
179
182
 
180
183
  await xc.setOutfit('xiaochun_cheongsam'); // 新服装生效后才 resolve
181
184
  await xc.setScene('transparent'); // 外壳背景和穿透开关自动跟随
@@ -185,7 +188,7 @@ xc.on('scene-changed', (p) => console.log(p.id));
185
188
 
186
189
  * **id 严格校验**(`/^[a-z][a-z0-9_]{0,63}$/` + 自有属性白名单)。格式不对本地直接 reject `[bad_request]`(不发消息);格式合法但不存在(`constructor`、`base` 等)reject `[unknown_id]`,iframe 一侧会独立再校验。
187
190
  * **并发**:换装**串行 + last-wins**。正在加载的不会被中止;排队中的请求被更新的调用顶掉时 reject `[busy]`(可忽略)。同目标请求合并;请求当前服装直接 resolve。**说话不会被打断**:正在说话时新服装在后台加载,好了再换上。
188
- * **场景**:只有 3 个内置主题。运行时切换会同步外壳背景、开关指针穿透监听、非透明场景强制 `pointer-events: auto`、重置命中缓存。
191
+ * **场景**:4 个内置场景 (`light` / `dark` / `transparent` / `beach3d`,其中只有 `transparent` 是透明的)。运行时切换会同步外壳背景、开关指针穿透监听、非透明场景强制 `pointer-events: auto`、重置命中缓存。
189
192
  * **能力协商**:协议仍是 v1。对旧版 `/embed`(`xc.ready` 里没有 `capabilities.outfits` / `scenes` / `prefetch`),新方法 reject `[unsupported]`,`getOutfits()` / `getScenes()` 返回 `[]`。
190
193
  * **偏好保存**:iframe 用**自己的** localStorage 记住用户最近的服装 / 场景(键 `xiaochun_wearing_outfit` / `xiaochun_scene_theme`,与主站一致)。优先级:显式的 `outfit` / `scene`(URL 或 SDK 选项)> 已保存 > 默认;存的 id 不在白名单会被忽略并清掉;存储被拦截 / 分区时静默回退默认。第三方存储按顶层站点分区,每个宿主站各一份。
191
194
  * **`persist`**(可选)另外把它们存在宿主页 localStorage,并作为显式值传回 iframe,因此会盖过 iframe 自己存的。默认 `false`。
@@ -363,7 +366,7 @@ createXiaochun({
363
366
  | `lazyMargin` | `200` | 可见性触发的 rootMargin(px)。调大更早加载、更耗流量 |
364
367
  | `placeholder` | 内置 SVG | 图片 URL、元素或 `false` |
365
368
  | `transparent` | `false` | 背景透明叠在页面上(同时开启指针穿透),等价于 `scene: 'transparent'` |
366
- | `scene` | — | 初始场景:`'light' \| 'dark' \| 'transparent'`(见 `getScenes()`)。未知 id 会被忽略并触发 `error { code: 'unknown_id' }` |
369
+ | `scene` | — | 初始场景:`'light' \| 'dark' \| 'transparent' \| 'beach3d'`(见 `getScenes()`)。未知 id 会被忽略并触发 `error { code: 'unknown_id' }` |
367
370
  | `width`、`height` | `600`、`1080` | px 或任意 CSS 长度。**默认值受视口限制**:宽 = `min(600px, 100vw)`(外壳另有 `max-width: 100%`,窄容器不溢出),高 = `min(1080px, 100svh)`(悬浮 `position` 还会扣掉 `--xc-offset-x/y` 边距,整块不会顶出屏幕);显式传入的值原样使用。运行时 `setSize(w, h)`(传 `undefined` = 恢复默认) |
368
371
  | `position` | `'inline'` | `'inline' \| 'bottom-right' \| 'bottom-left'` |
369
372
  | `draggable` | `false` | 手势拖动:按住角色(不要压在内置按钮上)拖 = 移动 iframe,限制在视口内;内联 / 悬浮模式都生效。见下文「手势」 |
@@ -388,7 +391,7 @@ createXiaochun({
388
391
  | `crossOriginIsolated` | `false` | 给 iframe 的 `allow` 追加 `cross-origin-isolated`(默认仍是 `microphone; autoplay`)。要求宿主页自己已跨源隔离,见 [可选:跨源隔离](#可选跨源隔离让-emage-用多线程) |
389
392
  | `zIndex` | `2147483000` | 悬浮模式层级(`--xc-z-index` 变量优先) |
390
393
 
391
- **实例**:`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`)*。
392
395
 
393
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'`。
394
397
 
@@ -398,7 +401,7 @@ createXiaochun({
398
401
  | :--- | :--- | :--- |
399
402
  | `src` | 官方 `/embed` | 修改会重建 iframe |
400
403
  | `outfit` | — | 服装 id;运行时修改 = `setOutfit`(**热更新,不重建 iframe**) |
401
- | `scene` | — | `light` · `dark` · `transparent`;运行时修改 = `setScene`(热更新) |
404
+ | `scene` | — | `light` · `dark` · `transparent` · `beach3d`;运行时修改 = `setScene`(热更新) |
402
405
  | `model` | — | `outfit` 的**已弃用**别名(`outfit` 优先) |
403
406
  | `camera-fov` / `camera-distance` / `camera-height` / `camera-intro` | — | 同 `camera`(热更新 `setConfig({ camera })`,不重建;去掉属性 = 恢复默认) |
404
407
  | `persist` / `persist-box` / `prefetch` / `allow-custom-model` | — | 同对应选项(`persist-box=""` = 默认 key,其它字符串 = 命名空间);修改会重建 |
@@ -420,7 +423,7 @@ createXiaochun({
420
423
  | `cross-origin-isolated` | `false` | 同 `createXiaochun({ crossOriginIsolated })`;修改会重建 iframe |
421
424
 
422
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`。
423
- **方法**:`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 实例。
424
427
 
425
428
  ---
426
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,18 +152,21 @@ 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 }); // streaming: e.g. PCM chunks from a TTS server
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.
163
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.
164
167
  * Rejects with `bad_request` (undecodable / empty audio, bad URL or sampleRate), `unsupported`, or `failed`.
165
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).
166
- * A newer `say()` / `speakAudio()` call preempts the one in progress, and the preempted promise resolves.
169
+ * A newer `say()` / `speakAudio()` fades out the line still playing (about 0.4s, skipped when the gain is already 0), then speaks the new one. The preempted promise resolves. `stop()`, an `AbortSignal`, or `xc.audio.end` with `{ abort: true }` cuts immediately. `say()` has no character cap; long text is split into short clauses inside the iframe before TTS.
167
170
  * `<xiaochun-avatar>` and the React ref expose the same `speakAudio` / `speakAudioStream` methods.
168
171
 
169
172
  ---
@@ -175,7 +178,7 @@ const xc = createXiaochun({ container: '#avatar', outfit: 'xiaochun_maid', scene
175
178
  await xc.ready;
176
179
 
177
180
  const outfits = await xc.getOutfits(); // [{ id, name }] (the bare base model is never listed)
178
- const scenes = await xc.getScenes(); // [{ id: 'light' | 'dark' | 'transparent', transparent }]
181
+ const scenes = await xc.getScenes(); // [{ id: 'light' | 'dark' | 'transparent' | 'beach3d', transparent }]
179
182
 
180
183
  await xc.setOutfit('xiaochun_cheongsam'); // resolves when the new outfit is live
181
184
  await xc.setScene('transparent'); // wrapper background + pass-through follow automatically
@@ -185,7 +188,7 @@ xc.on('scene-changed', (p) => console.log(p.id));
185
188
 
186
189
  * **Ids are validated** (`/^[a-z][a-z0-9_]{0,63}$/` + an own-property whitelist). A malformed id rejects locally with `[bad_request]`; a well-formed unknown id (`constructor`, `base`, ...) rejects with `[unknown_id]`. Nothing is sent for the former, and the iframe re-validates the latter on its own.
187
190
  * **Concurrency**: swaps are serial and **last-wins**. A swap that is loading is never aborted; a queued swap superseded by a newer call rejects with `[busy]` (safe to ignore). Same-target requests are merged; asking for the current outfit resolves immediately. **Speech is never interrupted**: if the avatar is talking, the new outfit loads in the background and appears when ready.
188
- * **Scenes**: only the three built-in themes. Switching at runtime syncs the wrapper background, enables/disables the pointer pass-through listeners, forces `pointer-events: auto` for opaque scenes and resets the hit cache.
191
+ * **Scenes**: 4 built-in scenes (`light` / `dark` / `transparent` / `beach3d`; only `transparent` is transparent). Switching at runtime syncs the wrapper background, enables/disables the pointer pass-through listeners, forces `pointer-events: auto` for opaque scenes and resets the hit cache.
189
192
  * **Capability negotiation**: protocol stays v1. Against an older `/embed` (no `capabilities.outfits` / `scenes` / `prefetch` in `xc.ready`), the new methods reject with `[unsupported]` and `getOutfits()` / `getScenes()` return `[]`.
190
193
  * **Preferences**: the iframe remembers the user's last outfit/scene in **its own** localStorage (`xiaochun_wearing_outfit` / `xiaochun_scene_theme`, the same keys as the main site). Priority: explicit `outfit` / `scene` (URL or SDK options) > saved > default; unknown saved ids are ignored and cleared; if storage is blocked or partitioned it silently falls back to the defaults. Third-party storage is partitioned per top-level site, so each host site has its own copy.
191
194
  * **`persist`** (optional) additionally keeps them in the host page's localStorage and passes them back as explicit values, so it overrides the iframe's own copy. Default `false`.
@@ -363,7 +366,7 @@ createXiaochun({
363
366
  | `lazyMargin` | `200` | rootMargin in px for the visibility trigger. Larger loads earlier and uses more data |
364
367
  | `placeholder` | built-in SVG | Image URL, element, or `false` |
365
368
  | `transparent` | `false` | Transparent background over your page (with pointer pass-through). Same as `scene: 'transparent'` |
366
- | `scene` | — | Initial scene: `'light' \| 'dark' \| 'transparent'` (see `getScenes()`). Unknown id → ignored + `error { code: 'unknown_id' }` |
369
+ | `scene` | — | Initial scene: `'light' \| 'dark' \| 'transparent' \| 'beach3d'` (see `getScenes()`). Unknown id → ignored + `error { code: 'unknown_id' }` |
367
370
  | `width`, `height` | `600`, `1080` | px or any CSS length. **Defaults are clamped to the viewport**: width = `min(600px, 100vw)` (and the shell has `max-width: 100%`, so it never overflows a narrow container), height = `min(1080px, 100svh)` (floating `position`s also subtract the `--xc-offset-x/y` margins so the box never sticks out of the screen). Explicit values are used as-is. Runtime: `setSize(w, h)` (`undefined` = back to the default) |
368
371
  | `position` | `'inline'` | `'inline' \| 'bottom-right' \| 'bottom-left'` |
369
372
  | `draggable` | `false` | Gesture drag: press and drag the character (not a built-in button) to move the iframe, clamped to the viewport. Works in inline and floating modes. See [Gestures](#gestures-drag-and-corner-resize) |
@@ -388,7 +391,7 @@ createXiaochun({
388
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) |
389
392
  | `zIndex` | `2147483000` | Floating mode layer (the `--xc-z-index` variable takes precedence) |
390
393
 
391
- **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`)*.
392
395
 
393
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'`.
394
397
 
@@ -398,7 +401,7 @@ createXiaochun({
398
401
  | :--- | :--- | :--- |
399
402
  | `src` | official `/embed` | Changing it rebuilds the iframe |
400
403
  | `outfit` | — | Outfit id; changing it at runtime = `setOutfit` (**live update, no iframe rebuild**) |
401
- | `scene` | — | `light` · `dark` · `transparent`; runtime change = `setScene` (live) |
404
+ | `scene` | — | `light` · `dark` · `transparent` · `beach3d`; runtime change = `setScene` (live) |
402
405
  | `model` | — | **Deprecated** alias of `outfit` (`outfit` wins) |
403
406
  | `camera-fov` / `camera-distance` / `camera-height` / `camera-intro` | — | Same as `camera` (hot-updated via `setConfig({ camera })`, no rebuild; removing an attribute = default) |
404
407
  | `persist` / `persist-box` / `prefetch` / `allow-custom-model` | — | Same as the options (`persist-box=""` = default key, any other string = namespace); changing them rebuilds |
@@ -420,7 +423,7 @@ createXiaochun({
420
423
  | `cross-origin-isolated` | `false` | Same as `createXiaochun({ crossOriginIsolated })`; changing it rebuilds the iframe |
421
424
 
422
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`.
423
- **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.
424
427
 
425
428
  ---
426
429
 
@@ -8,7 +8,7 @@
8
8
  * min-size / max-size="120x180": resizable 的最小 / 最大尺寸 (px, 缺省最小 120x180、最大只受视口限制)。
9
9
  * - border-radius: 外壳圆角 (数字 = px 或 CSS 长度); 缺省: 非透明场景 20px (与 Tauri 桌宠窗口同值) / 透明 0; 改属性热更新 (setBorderRadius)。
10
10
  * - outfit: 内置服装 id (xiaochun_maid); 改属性**热更新** (setOutfit, 不重建 iframe)。model 是 outfit 的旧别名 (deprecated)。
11
- * - scene: light | dark | transparent; 改属性**热更新** (setScene, 不重建 iframe)。
11
+ * - scene: light | dark | transparent | beach3d; 改属性**热更新** (setScene, 不重建 iframe)。
12
12
  * - ui: 要显示的 iframe 内置界面部件, 逗号分隔: chat,bubble,outfit,scene,lang,github (例 ui="outfit,scene,lang,github"); 缺省 = 都不显示; 改它会重建。
13
13
  * 旧写法 ui / ui="true" 已弃用 (= chat,bubble, 会 console.warn)。
14
14
  * - ui-autohide: 内置界面的显示策略: 缺省 / "transparent" = 与 Tauri 一致 (只在透明场景点击出现, 亮暗场景常显); "true" = 所有场景点击出现; "false" = 一直显示。改它会重建。
@@ -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.18 | MIT | https://github.com/FireTable/project-xiaochun */
1
+ /*! @firetable/project-xiaochun v0.1.20 | 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-KL7L3MGQ.js";
14
+ } from "./chunk-VPAQ4UMD.js";
15
15
 
16
16
  // src/gesture-box.ts
17
17
  var DEFAULT_RESIZE_LIMITS = Object.freeze({
@@ -189,7 +189,7 @@ function createXiaochun(options) {
189
189
  const outfitOpt = options.outfit ?? (isXcId(options.model) ? options.model : void 0);
190
190
  const initialOutfit = outfitOpt ?? prefs.outfit;
191
191
  const initialScene = options.scene ?? prefs.scene ?? (options.transparent ? "transparent" : void 0);
192
- let transparent = initialScene === "transparent" || initialScene !== "light" && initialScene !== "dark" && (options.transparent ?? false);
192
+ let transparent = initialScene === "transparent" || initialScene !== "light" && initialScene !== "dark" && initialScene !== "beach3d" && (options.transparent ?? false);
193
193
  let passthrough = options.passthrough ?? transparent;
194
194
  const lazy = options.lazy ?? true;
195
195
  const position = options.position ?? "inline";
@@ -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-BN4H52OR.js.map
1094
+ //# sourceMappingURL=chunk-OUHLLXLG.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/gesture-box.ts", "../../src/client.ts"],
4
+ "sourcesContent": ["/**\n * gesture-box.ts \u2014 iframe \u624B\u52BF (xc.gesture-move / xc.gesture-resize) \u5728\u5BBF\u4E3B\u4FA7\u7684\u7EAF\u903B\u8F91: \u6821\u9A8C + \u51E0\u4F55\u9650\u5E45\u3002\u96F6 DOM \u4F9D\u8D56, \u53EF\u5355\u6D4B\u3002\n *\n * \u5BBF\u4E3B SDK \u5BF9 iframe \u53D1\u6765\u7684\u624B\u52BF**\u4E0D\u4FE1\u4EFB**: \u8F7D\u8377\u9010\u5B57\u6BB5\u6821\u9A8C (\u7C7B\u578B / \u6709\u9650\u6570 / \u91CF\u7EA7 / \u89D2\u540D), \u5E8F\u53F7\u5FC5\u987B\u6309\u89C4\u5219\u9012\u589E (GestureGate),\n * \u4F4D\u7F6E / \u5C3A\u5BF8\u5168\u90E8\u5728\u8FD9\u91CC\u6309\"\u5F53\u524D\u89C6\u53E3 + \u6700\u5C0F / \u6700\u5927\u5C3A\u5BF8\"\u5939\u7D27, iframe \u8BF4\u591A\u5C11\u5C31\u79FB\u591A\u5C11\u662F\u4E0D\u53EF\u80FD\u7684\u3002\n * \u8BA1\u7B97\u4E00\u5F8B\u7528 start \u65F6\u7684\u76D2\u5B50 + \u7D2F\u8BA1\u4F4D\u79FB (totalDx / totalDy), \u4E0D\u7D2F\u52A0\u589E\u91CF: \u4E22\u5305 / \u5939\u7D27\u90FD\u4E0D\u4F1A\u8BA9\u7ED3\u679C\u6F02\u79FB\u3002\n */\nimport type { XcGestureCorner } from './protocol';\n\nexport interface Box { left: number; top: number; width: number; height: number }\nexport interface Viewport { width: number; height: number }\nexport interface ResizeLimits { minWidth: number; minHeight: number; maxWidth: number; maxHeight: number }\n\n/** \u9ED8\u8BA4\u9650\u5E45: \u6700\u5C0F 120x180 (\u518D\u5C0F\u89D2\u8272\u57FA\u672C\u770B\u4E0D\u6E05), \u6700\u5927\u53EA\u53D7\u89C6\u53E3\u9650\u5236\u3002 */\nexport const DEFAULT_RESIZE_LIMITS: Readonly<ResizeLimits> = Object.freeze({\n minWidth: 120, minHeight: 180, maxWidth: Infinity, maxHeight: Infinity,\n});\n\nexport interface XiaochunResizeLimits {\n /** \u6700\u5C0F\u5BBD (px), \u9ED8\u8BA4 120\u3002 */\n minWidth?: number;\n /** \u6700\u5C0F\u9AD8 (px), \u9ED8\u8BA4 180\u3002 */\n minHeight?: number;\n /** \u6700\u5927\u5BBD (px), \u9ED8\u8BA4\u53EA\u53D7\u89C6\u53E3\u9650\u5236\u3002 */\n maxWidth?: number;\n /** \u6700\u5927\u9AD8 (px), \u9ED8\u8BA4\u53EA\u53D7\u89C6\u53E3\u9650\u5236\u3002 */\n maxHeight?: number;\n}\n\nconst finitePositive = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v) && v > 0;\n\n/** `resizable` \u9009\u9879 \u2192 \u9650\u5E45; false / undefined \u2192 null (\u5173\u95ED); true \u2192 \u9ED8\u8BA4\u9650\u5E45; \u5BF9\u8C61 \u2192 \u9010\u9879\u8986\u76D6 (\u975E\u6CD5\u503C\u7528\u9ED8\u8BA4)\u3002min \u4E0D\u4F1A\u8D85\u8FC7 max\u3002 */\nexport function normalizeResizable(v: boolean | XiaochunResizeLimits | undefined): ResizeLimits | null {\n if (!v) return null;\n const o = v === true ? {} : v;\n const minWidth = finitePositive(o.minWidth) ? o.minWidth : DEFAULT_RESIZE_LIMITS.minWidth;\n const minHeight = finitePositive(o.minHeight) ? o.minHeight : DEFAULT_RESIZE_LIMITS.minHeight;\n const maxWidth = finitePositive(o.maxWidth) ? Math.max(o.maxWidth, minWidth) : Infinity;\n const maxHeight = finitePositive(o.maxHeight) ? Math.max(o.maxHeight, minHeight) : Infinity;\n return { minWidth, minHeight, maxWidth, maxHeight };\n}\n\nconst clamp = (v: number, lo: number, hi: number) => Math.min(Math.max(v, lo), Math.max(lo, hi));\n\n/** \u79FB\u52A8: \u8D77\u70B9\u76D2\u5B50 + \u7D2F\u8BA1\u4F4D\u79FB, \u5939\u5728\u89C6\u53E3\u5185 (\u76D2\u5B50\u6BD4\u89C6\u53E3\u5927\u65F6\u8D34\u5DE6\u4E0A)\u3002 */\nexport function moveBox(start: Box, totalDx: number, totalDy: number, vp: Viewport): Box {\n return {\n left: clamp(start.left + totalDx, 0, vp.width - start.width),\n top: clamp(start.top + totalDy, 0, vp.height - start.height),\n width: start.width,\n height: start.height,\n };\n}\n\n/**\n * \u7F29\u653E: \u88AB\u62D6\u7684\u89D2\u8DDF\u624B, \u5BF9\u89D2\u56FA\u5B9A; \u5BBD\u9AD8\u5939\u5728 [min, min(max, \u89C6\u53E3\u5185\u8FD8\u80FD\u4F38\u5C55\u7684\u7A7A\u95F4)]\u3002\n * \u4F8B: \u62D6 SE \u89D2 \u2192 \u5DE6\u4E0A\u56FA\u5B9A, \u53F3 / \u4E0B\u8FB9\u754C\u4E0D\u80FD\u8D8A\u8FC7\u89C6\u53E3; \u62D6 NW \u89D2 \u2192 \u53F3\u4E0B\u56FA\u5B9A, \u5DE6 / \u4E0A\u8FB9\u754C\u4E0D\u80FD\u8D8A\u8FC7\u89C6\u53E3\u3002\n */\nexport function resizeBox(start: Box, corner: XcGestureCorner, totalDx: number, totalDy: number, limits: ResizeLimits, vp: Viewport): Box {\n const west = corner === 'NW' || corner === 'SW';\n const north = corner === 'NW' || corner === 'NE';\n const right = start.left + start.width, bottom = start.top + start.height;\n const wantW = west ? start.width - totalDx : start.width + totalDx;\n const wantH = north ? start.height - totalDy : start.height + totalDy;\n const roomW = west ? right : vp.width - start.left;\n const roomH = north ? bottom : vp.height - start.top;\n const width = clamp(wantW, limits.minWidth, Math.min(limits.maxWidth, roomW));\n const height = clamp(wantH, limits.minHeight, Math.min(limits.maxHeight, roomH));\n return { left: west ? right - width : start.left, top: north ? bottom - height : start.top, width, height };\n}\n\n/** \u7A97\u53E3 / \u89C6\u53E3\u53D8\u5C0F\u540E\u628A\u76D2\u5B50\u91CD\u65B0\u5939\u56DE\u89C6\u53E3 (\u5C3A\u5BF8\u4E5F\u4E0D\u8D85\u8FC7\u89C6\u53E3)\u3002 */\nexport function fitBox(b: Box, vp: Viewport): Box {\n const width = Math.min(b.width, Math.max(1, vp.width)), height = Math.min(b.height, Math.max(1, vp.height));\n return { left: clamp(b.left, 0, vp.width - width), top: clamp(b.top, 0, vp.height - height), width, height };\n}\n\n// \u2500\u2500 \u8F7D\u8377\u6821\u9A8C \u2500\u2500\nconst CORNERS = new Set(['NW', 'NE', 'SW', 'SE']);\nconst PHASES = new Set(['start', 'move', 'end']);\nconst REASONS = new Set(['up', 'cancel', 'blur']);\n/** \u5355\u6B21\u7D2F\u8BA1\u4F4D\u79FB\u7684\u5408\u7406\u4E0A\u9650 (CSS px)\u3002\u5C4F\u5E55\u518D\u5927\u4E5F\u5230\u4E0D\u4E86, \u8D85\u8FC7\u7684\u4E00\u5F8B\u5F53\u4F5C\u4F2A\u9020\u3002 */\nexport const MAX_GESTURE_DELTA = 20_000;\n\nexport interface ParsedGesture {\n gesture: number;\n seq: number;\n phase: 'start' | 'move' | 'end';\n totalDx: number;\n totalDy: number;\n corner?: XcGestureCorner;\n}\n\nconst delta = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v) && Math.abs(v) <= MAX_GESTURE_DELTA;\nconst counter = (v: unknown): v is number => typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;\n\n/** \u6821\u9A8C iframe \u53D1\u6765\u7684\u624B\u52BF\u8F7D\u8377; \u4E0D\u5408\u6CD5\u8FD4\u56DE null\u3002kind='resize' \u8FD8\u8981\u6C42\u5408\u6CD5\u7684 corner\u3002 */\nexport function parseGesturePayload(kind: 'move' | 'resize', raw: unknown): ParsedGesture | null {\n if (raw === null || typeof raw !== 'object') return null;\n const p = raw as Record<string, unknown>;\n if (!counter(p.gesture) || !counter(p.seq) || typeof p.phase !== 'string' || !PHASES.has(p.phase)) return null;\n if (!delta(p.dx) || !delta(p.dy) || !delta(p.totalDx) || !delta(p.totalDy)) return null;\n if (p.reason !== undefined && (typeof p.reason !== 'string' || !REASONS.has(p.reason))) return null;\n const out: ParsedGesture = { gesture: p.gesture, seq: p.seq, phase: p.phase as ParsedGesture['phase'], totalDx: p.totalDx, totalDy: p.totalDy };\n if (kind === 'resize') {\n if (typeof p.corner !== 'string' || !CORNERS.has(p.corner)) return null;\n out.corner = p.corner as XcGestureCorner;\n }\n return out;\n}\n\n/**\n * \u624B\u52BF\u5E8F\u53F7\u95F8\u95E8: \u53EA\u653E\u884C\"\u5408\u6CD5\u7684\u624B\u52BF\u5E8F\u5217\" \u2014\u2014 start(gesture \u6BD4\u4E0A\u4E00\u4E2A\u5927, seq=0) \u2192 move* (seq \u4E25\u683C\u9012\u589E) \u2192 end\u3002\n * \u8FDF\u5230 / \u91CD\u653E / \u4E71\u5E8F / \u6CA1\u6709 start \u7684\u6D88\u606F\u4E00\u5F8B\u62D2\u7EDD\u3002\u4E00\u4E2A\u95F8\u95E8\u5BF9\u5E94\u4E00\u79CD\u624B\u52BF (move \u6216 resize)\u3002\n */\nexport class GestureGate {\n private lastGesture = 0;\n private lastSeq = -1;\n private open = false;\n\n get active(): boolean { return this.open; }\n\n accept(p: Pick<ParsedGesture, 'gesture' | 'seq' | 'phase'>): boolean {\n if (p.phase === 'start') {\n if (p.gesture <= this.lastGesture || p.seq !== 0) return false;\n this.lastGesture = p.gesture;\n this.lastSeq = 0;\n this.open = true;\n return true;\n }\n if (!this.open || p.gesture !== this.lastGesture || p.seq <= this.lastSeq) return false;\n this.lastSeq = p.seq;\n if (p.phase === 'end') this.open = false;\n return true;\n }\n\n /** \u5BBF\u4E3B\u4E3B\u52A8\u7ED3\u675F (\u5173\u95ED\u624B\u52BF / \u9500\u6BC1): \u4E4B\u540E\u540C\u4E00\u624B\u52BF\u7684\u8FDF\u5230\u6D88\u606F\u88AB\u62D2\u7EDD\u3002 */\n close(): void { this.open = false; }\n}\n", "/**\n * client.ts \u2014 \u65E0\u6846\u67B6\u3001\u96F6\u8FD0\u884C\u65F6\u4F9D\u8D56\u7684 Project XiaoChun \u5D4C\u5165 SDK\u3002\n *\n * const xc = createXiaochun({ container: '#avatar', transparent: true });\n * await xc.ready; // \u6A21\u578B\u52A0\u8F7D\u5B8C\u6210 (xc.loaded)\n * await xc.say('\u4F60\u597D\u5440'); // \u5FF5\u5B8C (xc.utterance end) \u624D resolve\n *\n * \u9ED8\u8BA4 lazy: \u5148\u653E\u56FA\u5B9A\u5C3A\u5BF8\u7684\u5360\u4F4D (\u4E0D\u4EA7\u751F CLS), \u6D4F\u89C8\u5668\u7A7A\u95F2 + \u8FDB\u5165\u89C6\u53E3 + (\u6216\u70B9\u51FB/\u8C03\u7528 API) \u4E4B\u540E\u624D\u521B\u5EFA iframe\u3002\n */\nimport {\n XC_DEFAULT_SRC,\n isXcEnvelope,\n isXcId,\n normalizeOrigin,\n normalizeXcUiOption,\n parseXcUiAutoHide,\n XC_UI_PARTS,\n XC_CAMERA_RANGES,\n normalizeXcCamera,\n XC_WINDOW_CORNER_RADIUS,\n xcMessage,\n type XcAudioFormat,\n type XcAudioOptions,\n type XcCamera,\n type XcConfig,\n type XcErrorCode,\n type XcExpressionPayload,\n type XcFrameMessage,\n type XcHeavyMode,\n type XcHitRegionPayload,\n type XcHostMessageType,\n type XcLang,\n type XcLangChangedPayload,\n type XcLoadProgressPayload,\n type XcMotionPayload,\n type XcOutfitChangedPayload,\n type XcOutfitInfo,\n type XcPrefetchedPayload,\n type XcReadyPayload,\n type XcSceneChangedPayload,\n type XcSceneId,\n type XcSceneInfo,\n type XcSttPayload,\n type XcStatePayload,\n type XcUiAutoHide,\n type XcUiPart,\n type XcUtterancePayload,\n} from './protocol';\nimport {\n DEFAULT_RESIZE_LIMITS, GestureGate, fitBox, moveBox, normalizeResizable, parseGesturePayload, resizeBox,\n type Box, type ResizeLimits, type Viewport, type XiaochunResizeLimits,\n} from './gesture-box';\n\nexport type { XiaochunResizeLimits } from './gesture-box';\nexport type XiaochunPosition = 'inline' | 'bottom-right' | 'bottom-left';\n/** lazy: false=\u7ACB\u5373\u521B\u5EFA; true/'idle'=\u8FDB\u5165\u89C6\u53E3\u4E14\u7A7A\u95F2\u540E\u521B\u5EFA; 'click'=\u4EC5\u70B9\u51FB\u5360\u4F4D\u6216\u8C03\u7528 API \u65F6\u521B\u5EFA\u3002 */\nexport type XiaochunLazy = boolean | 'idle' | 'click';\n\nexport interface XiaochunOptions {\n /** \u6302\u8F7D\u5BB9\u5668 (\u5143\u7D20\u6216\u9009\u62E9\u5668)\u3002 */\n container: HTMLElement | string;\n /** /embed \u9875\u9762\u5730\u5740, \u9ED8\u8BA4\u5B98\u65B9\u90E8\u7F72\u3002\u81EA\u5EFA/\u672C\u5730\u8C03\u8BD5\u65F6\u8986\u76D6\u3002 */\n src?: string;\n /** iframe \u7684 origin, \u9ED8\u8BA4\u7531 src \u63A8\u5BFC\u3002\u6240\u6709\u63E1\u624B/\u6D88\u606F\u6821\u9A8C\u90FD\u4EE5\u5B83\u4E3A\u51C6\u3002 */\n origin?: string;\n /** \u989D\u5916\u5141\u8BB8\u53D1 xc.ready \u7684 origin (\u5982 CDN \u91CD\u5B9A\u5411); \u9ED8\u8BA4\u53EA\u6709 origin\u3002\u4E0D\u652F\u6301 '*'\u3002 */\n allowedOrigins?: string[];\n lazy?: XiaochunLazy;\n /** \u8DDD\u89C6\u53E3\u591A\u5C11 px \u5185\u5C31\u5F00\u59CB\u52A0\u8F7D (IntersectionObserver rootMargin)\u3002\u8C03\u5927=\u66F4\u65E9\u52A0\u8F7D, \u66F4\u8017\u6D41\u91CF; \u8C03\u5C0F=\u66F4\u7701\u4F46\u6EDA\u5230\u65F6\u53EF\u80FD\u8FD8\u6CA1\u597D\u3002\u9ED8\u8BA4 200\u3002 */\n lazyMargin?: number;\n /** \u5360\u4F4D\u56FE URL / \u5143\u7D20; false = \u4E0D\u8981\u5360\u4F4D\u3002 */\n placeholder?: string | HTMLElement | false;\n /**\n * \u80CC\u666F\u900F\u660E (\u53E0\u5728\u5BBF\u4E3B\u9875\u4E0A)\u3002\u7B49\u4EF7\u4E8E `scene: 'transparent'`; \u540C\u65F6\u7ED9\u4E86 `scene` \u6216\u5DF2\u4FDD\u5B58\u7684\u504F\u597D\u65F6, \u4EE5\u5B83\u4EEC\u4E3A\u51C6\u3002\n * \u8FD0\u884C\u4E2D\u60F3\u5207\u6362\u8BF7\u7528 `setScene()`: SDK \u4F1A\u540C\u6B65\u5916\u58F3\u80CC\u666F\u548C\u7A7F\u900F\u3002\n */\n transparent?: boolean;\n /** \u521D\u59CB\u573A\u666F: 'light' | 'dark' | 'transparent' | 'beach3d' (\u4EE5 getScenes() \u4E3A\u51C6)\u3002\u7701\u7565 = \u8DDF\u968F transparent \u9009\u9879 / \u7CFB\u7EDF\u4EAE\u6697\u3002 */\n scene?: XcSceneId | (string & {});\n /**\n * \u56FA\u5B9A\u5C3A\u5BF8, \u6570\u5B57=px, \u5B57\u7B26\u4E32=CSS \u957F\u5EA6\u3002\u9ED8\u8BA4 600x1080 (\u5BBD\u9AD8\u6BD4 \u2248 0.556, \u4E0E\u684C\u9762\u7AEF\u7A97\u53E3\u7684\u7AD6\u7248\u6BD4\u4F8B\u540C\u4E00\u91CF\u7EA7)\u3002\n * **\u9ED8\u8BA4\u503C\u53D7\u89C6\u53E3\u9650\u5236**: \u5BBD = min(600px, \u89C6\u53E3\u5BBD) \u4E14\u5916\u58F3 max-width:100% (\u4E0D\u8D85\u8FC7\u5BB9\u5668\u5BBD), \u9AD8 = min(1080px, \u89C6\u53E3\u9AD8), \u7A84\u5C4F / \u5C0F\u7A97\u53E3\u4E0D\u4F1A\u6EA2\u51FA; \u663E\u5F0F\u7ED9\u7684\u503C\u539F\u6837\u4F7F\u7528 (\u5BBD\u4ECD\u6709 max-width:100%)\u3002\n * \u52A1\u5FC5\u7ED9\u5B9A\u6216\u63A5\u53D7\u9ED8\u8BA4\u503C, \u5426\u5219\u65E0\u6CD5\u9884\u7559\u7A7A\u95F4 (CLS)\u3002\u8FD0\u884C\u65F6\u7528 setSize() \u6539\u3002\n */\n width?: number | string;\n height?: number | string;\n position?: XiaochunPosition;\n /**\n * \u5141\u8BB8\u7528\u6237\u62D6\u52A8\u5934\u50CF, \u9ED8\u8BA4 false\u3002**\u624B\u52BF\u62D6\u52A8**: \u5728\u89D2\u8272\u4E0A\u6309\u4F4F\u5DE6\u952E\u62D6 (\u89E6\u5C4F: \u624B\u6307\u62D6) = \u79FB\u52A8\u6574\u4E2A\u5934\u50CF (\u8BC6\u522B\u5728 iframe \u91CC, \u590D\u7528 Tauri \u684C\u5BA0\u7684\u624B\u52BF\u72B6\u6001\u673A,\n * \u5BBF\u4E3B\u6267\u884C\u79FB\u52A8\u5E76\u9650\u5E45\u5728\u89C6\u53E3\u5185); \u900F\u660E + \u7A7F\u900F\u573A\u666F\u4E0B\u53EA\u6709\u70B9\u5728\u89D2\u8272\u4E0A\u624D\u63A5\u7BA1, \u5176\u4F59\u4F4D\u7F6E\u4ECD\u7A7F\u900F\u7ED9\u5BBF\u4E3B\u9875\u3002\u5185\u8054 (position: 'inline') \u7528 CSS translate \u4F4D\u79FB, \u4E0D\u6539\u53D8\u5E03\u5C40\u6D41\u3002\n * \u60AC\u6D6E\u6A21\u5F0F (bottom-right / bottom-left) \u540C\u6837\u8D70\u624B\u52BF (\u6539 left/top)\u3002\u8FD0\u884C\u4E2D\u53EF\u7528 `setDraggable()` \u5F00\u5173, \u4E0D\u91CD\u5EFA iframe\u3002\n * \u9700\u8981 /embed \u7684 capabilities.gestures (\u65E7\u7248\u6CA1\u6709 \u2192 \u624B\u52BF\u65E0\u6548, \u5E76\u89E6\u53D1\u4E00\u6B21 error{code:'unsupported'})\u3002\n */\n draggable?: boolean;\n /**\n * \u5916\u58F3\u5706\u89D2 (iframe / \u5360\u4F4D\u56FE / \u5916\u58F3\u4E00\u8D77\u88C1), \u6570\u5B57 = px, \u6216\u4EFB\u610F CSS \u957F\u5EA6\u3002\u9ED8\u8BA4: **\u975E\u900F\u660E\u573A\u666F (light / dark) = 20px** (\u4E0E Tauri \u684C\u5BA0\u7A97\u53E3\u7684\u5706\u89D2\u540C\u503C,\n * \u89C1 XC_WINDOW_CORNER_RADIUS), \u900F\u660E\u573A\u666F = 0 (\u6CA1\u6709\u5E95\u8272\u53EF\u88C1, \u89D2\u8272\u4E0D\u88AB\u88C1\u89D2)\u3002`--xc-radius` CSS \u53D8\u91CF\u4F18\u5148\u7EA7\u66F4\u9AD8\u3002\u8FD0\u884C\u4E2D\u53EF\u7528 `setBorderRadius()` \u6539, \u4E0D\u91CD\u5EFA iframe\u3002\n * \u5706\u89D2\u534A\u5F84\u662F\u56FA\u5B9A px, \u7F29\u653E (resizable) \u65F6\u4E0D\u968F\u5C3A\u5BF8\u53D8\u5F62\u3002\u4F20 0 \u6062\u590D\u65B9\u89D2\u3002\n */\n borderRadius?: number | string;\n /**\n * \u5141\u8BB8\u7528\u6237\u7F29\u653E\u5934\u50CF, \u9ED8\u8BA4 false\u3002**\u89D2\u843D\u7F29\u653E**: iframe \u56DB\u4E2A\u89D2\u5404\u6709\u4E00\u4E2A 40px \u70ED\u533A (\u60AC\u505C\u51FA\u73B0\u5706\u5F27\u63D0\u793A, \u5149\u6807\u53D8\u6210\u7F29\u653E\u7BAD\u5934), \u6309\u4F4F\u62D6\u52A8 = \u7F29\u653E, \u5BF9\u89D2\u56FA\u5B9A\u3002\n * true = \u9ED8\u8BA4\u9650\u5E45 (\u6700\u5C0F 120x180, \u6700\u5927\u53EA\u53D7\u89C6\u53E3\u9650\u5236); \u5BF9\u8C61\u53EF\u81EA\u5B9A\u4E49 minWidth / minHeight / maxWidth / maxHeight (px)\u3002\n * \u7F29\u653E\u53EA\u6539\u5916\u58F3\u5C3A\u5BF8, **\u4E0D\u91CD\u5EFA iframe**: \u6A21\u578B / \u52A8\u753B / \u5BF9\u8BDD\u72B6\u6001\u90FD\u4FDD\u6301\u3002\u901A\u8FC7 `resize` \u4E8B\u4EF6\u62FF\u5230\u65B0\u5C3A\u5BF8, `getBox()` \u968F\u65F6\u8BFB\u53D6\u3002\u8FD0\u884C\u4E2D\u53EF\u7528 `setResizable()` \u5F00\u5173\u3002\n * \u9700\u8981 capabilities.gestures (\u65E7\u7248 /embed \u65E0\u6548, \u89E6\u53D1\u4E00\u6B21 error{code:'unsupported'})\u3002\n */\n resizable?: boolean | XiaochunResizeLimits;\n /**\n * iframe \u754C\u9762\u8BED\u8A00 ('zh-CN' | 'en' | 'ja')\u3002\u663E\u5F0F\u6307\u5B9A\u65F6\u4F18\u5148\u4E8E iframe \u81EA\u5DF1\u8BB0\u4F4F\u7684\u7528\u6237\u9009\u62E9; \u4E0D\u5199 = \u7528 iframe localStorage \u91CC\u7528\u6237\u4E0A\u6B21\u5728\u8BED\u8A00\u6309\u94AE\u91CC\u9009\u7684, \u518D\u6CA1\u6709\u5C31\u8DDF\u968F\u6D4F\u89C8\u5668\u8BED\u8A00 (\u9ED8\u8BA4 zh-CN)\u3002\n * \u8FD0\u884C\u4E2D\u53EF\u7528 `setConfig({ lang })` \u6539 (\u4E0D\u6301\u4E45\u5316); \u7528\u6237\u70B9\u5185\u7F6E\u8BED\u8A00\u6309\u94AE (`ui` \u91CC\u7684 'lang') \u4F1A\u5B58\u8FDB iframe \u81EA\u5DF1\u7684 localStorage, \u5E76\u53D1 `lang-changed` \u4E8B\u4EF6\u3002\n */\n lang?: XcLang;\n /** \u521D\u59CB\u670D\u88C5: \u5185\u7F6E\u670D\u88C5 id (\u5982 'xiaochun_maid', \u89C1 getOutfits())\u3002\u672A\u77E5 id \u56DE\u9000\u9ED8\u8BA4\u670D\u88C5, \u5E76\u89E6\u53D1 error{code:'unknown_id'}\u3002 */\n outfit?: string;\n /**\n * @deprecated \u8BF7\u6539\u7528 `outfit`\u3002\u503C\u662F\u5185\u7F6E\u670D\u88C5 id \u65F6\u7B49\u4EF7\u4E8E `outfit`; https URL \u4E0D\u80FD\u518D\u901A\u8FC7\u521B\u5EFA\u9009\u9879\u4F20\u5165\n * (\u4EFB\u610F URL \u9700\u8981 `allowCustomModel: true`, \u4E4B\u540E\u7528 `setModel({ url })`)\u3002\n */\n model?: string;\n /**\n * \u5141\u8BB8 `setModel({ url })` \u52A0\u8F7D\u4EFB\u610F https .vrm/.vrmaddon/.vrmbase, \u9ED8\u8BA4 false (\u5173\u95ED)\u3002\n * \u6A21\u578B\u662F\u7B2C\u4E09\u65B9\u6587\u4EF6, \u4F1A\u5728 iframe \u91CC\u88AB\u89E3\u6790, \u53EA\u5728\u4F60\u4FE1\u4EFB\u8BE5 URL \u65F6\u6253\u5F00\u3002\u5185\u7F6E\u670D\u88C5 (`setOutfit`) \u4E0D\u53D7\u5F71\u54CD\u3002\n */\n allowCustomModel?: boolean;\n /**\n * \u504F\u597D (\u670D\u88C5 + \u573A\u666F) \u5728\u5BBF\u4E3B\u9875\u7684\u989D\u5916\u4FDD\u5B58 (\u53EF\u9009), \u9ED8\u8BA4 false\u3002\u4FDD\u5B58\u4F4D\u7F6E\u662F**\u5BBF\u4E3B\u9875**\u7684 localStorage; \u8BFB\u51FA\u6765\u540E\u4F5C\u4E3A\u663E\u5F0F\u503C\u4F20\u7ED9 iframe, \u6240\u4EE5\u4F1A\u76D6\u8FC7 iframe \u81EA\u5DF1 localStorage \u91CC\u5B58\u7684 (iframe \u672C\u6765\u5C31\u4F1A\u8BB0\u4F4F\u7528\u6237\u7684\u9009\u62E9, \u4F46\u5B83\u7684\u5B58\u50A8\u53EF\u80FD\u88AB\u6D4F\u89C8\u5668\u5206\u533A)\u3002\n * false \u4E0D\u4FDD\u5B58\n * 'host' \u4FDD\u5B58\u5230 localStorage['xiaochun:prefs']\n * \u5176\u5B83\u5B57\u7B26\u4E32 \u4FDD\u5B58\u5230 localStorage[\u8BE5\u5B57\u7B26\u4E32] (\u540C\u9875\u591A\u4E2A\u5B9E\u4F8B\u4E92\u4E0D\u8986\u76D6\u65F6\u7528)\n * \u4F18\u5148\u7EA7: \u663E\u5F0F\u7684 outfit / scene \u9009\u9879 > \u5DF2\u4FDD\u5B58\u7684\u504F\u597D > \u9ED8\u8BA4\u3002\u53EA\u6709 setOutfit / setScene \u5F15\u8D77\u7684\u53D8\u5316\u4F1A\u5199\u5165\u3002\n */\n persist?: false | 'host' | (string & {});\n /**\n * \u8BB0\u4F4F\u7528\u6237\u62D6\u52A8 / \u7F29\u653E\u540E\u7684**\u4F4D\u7F6E\u548C\u5927\u5C0F**\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4 false = \u4E0D\u8BB0\uFF09\u3002\u4FDD\u5B58\u5230**\u5BBF\u4E3B\u9875**\u7684 localStorage\uFF08\u8BFB\u5199\u90FD\u5305\u4E86 try/catch\uFF0C\u88AB\u7981\u7528 / \u914D\u989D\u6EE1\u53EA\u662F\u4E0D\u8BB0\u5FC6\uFF09\uFF0C\n * \u5728 draggable / resizable \u624B\u52BF**\u7ED3\u675F**\u65F6\u5199\u5165\uFF0C\u4E0B\u6B21\u521B\u5EFA\u65F6\u6062\u590D\u3002\n * false / \u4E0D\u5199 \u4E0D\u8BB0\n * true \u5B58\u5230 localStorage['xiaochun:box']\n * '<\u540D\u5B57>' \u5B58\u5230 localStorage['xiaochun:box:<\u540D\u5B57>']\uFF08\u540C\u4E00\u7AD9\u70B9\u591A\u4E2A\u5B9E\u4F8B / \u591A\u4E2A\u9875\u9762\u4E92\u4E0D\u8986\u76D6\u65F6\u7528\uFF1B\u4E5F\u53EF\u5F53\u7248\u672C\u53F7\uFF0C\u6539\u540D\u5373\u4E22\u5F03\u65E7\u503C\uFF09\n * \u4F18\u5148\u7EA7\uFF1A\u663E\u5F0F\u7684 width / height \u9009\u9879 > \u5DF2\u4FDD\u5B58\u7684 > \u9ED8\u8BA4\uFF08600x1080\uFF09\u3002\u4F4D\u7F6E\u6CA1\u6709\u5BF9\u5E94\u7684\u663E\u5F0F\u9009\u9879\uFF08`position` \u53EA\u662F\u9884\u8BBE\u951A\u70B9\uFF09\uFF0C\u5DF2\u4FDD\u5B58\u7684\u4F4D\u7F6E\u59CB\u7EC8\u751F\u6548\u3002\n * \u6240\u4EE5\u60F3\u8BA9\"\u7528\u6237\u7F29\u653E\u8FC7\u7684\u5927\u5C0F\"\u88AB\u6062\u590D\uFF0C\u5C31**\u4E0D\u8981**\u4F20 width / height\uFF08\u7528\u9ED8\u8BA4\u503C\uFF09\u3002\n * \u53EA\u5728\u5BF9\u5E94\u624B\u52BF\u6253\u5F00\u65F6\u6062\u590D\uFF08\u9700\u8981 draggable \u6216 resizable\uFF1B\u6CA1\u5F00\u624B\u52BF\u7684\u5B9E\u4F8B\u4E0D\u4F1A\u88AB\u65E7\u503C\u6539\u4F4D\u7F6E\uFF09\uFF1B\u4FDD\u5B58\u7684 `position` \u6A21\u5F0F\uFF08inline / bottom-right / bottom-left\uFF09\u4E0E\u5F53\u524D\u4E0D\u540C\u5219\u5FFD\u7565\u3002\n * \u6062\u590D\u65F6\u94B3\u5236\u5230**\u5F53\u524D\u89C6\u53E3**\uFF1A\u5927\u5C0F\u5939\u5728 [\u6700\u5C0F, \u89C6\u53E3]\uFF08\u6700\u5C0F = resizable \u7684 minWidth/minHeight\uFF0C\u9ED8\u8BA4 120x180\uFF09\uFF0C\u60AC\u6D6E\u6A21\u5F0F\u7684\u4F4D\u7F6E\u5939\u8FDB\u89C6\u53E3\uFF1B\n * \u5185\u8054\u6A21\u5F0F\u6A2A\u5411\u5939\u8FDB\u89C6\u53E3\u3001\u7EB5\u5411\u53EA\u4FDD\u8BC1\u4E0D\u8D8A\u8FC7\u6587\u6863\u9876\u90E8\uFF08\u5185\u8054\u76D2\u5B50\u53EF\u4EE5\u5728\u6EDA\u52A8\u7684\u9875\u9762\u91CC\uFF09\u3002\u7A97\u53E3\u4E4B\u540E\u53D8\u5C0F\u65F6\uFF0C\u65E2\u6709\u7684 resize \u76D1\u542C\u4E5F\u4F1A\u7EE7\u7EED\u628A\u5B83\u5939\u56DE\u89C6\u53E3\u3002\n * \u6E05\u9664\uFF1A`clearPersistedBox()`\uFF08\u53EA\u5220\u5B58\u50A8\uFF09\u6216 `clearPersistedBox({ reset: true })`\uFF08\u540C\u65F6\u56DE\u5230\u521D\u59CB\u4F4D\u7F6E / \u5927\u5C0F\uFF09\u3002\n */\n persistBox?: boolean | (string & {});\n /**\n * \u8981\u663E\u793A\u7684 iframe \u5185\u7F6E\u754C\u9762\u90E8\u4EF6 (\u6570\u7EC4, \u9ED8\u8BA4\u4E0D\u5199 = \u5168\u4E0D\u663E\u793A):\n * 'chat' \u5E95\u90E8\u804A\u5929\u680F (\u5BF9\u8BDD\u8D70 WebLLM, \u4F1A\u591A\u4E0B\u8F7D\u4E00\u90E8\u5206\u4EE3\u7801)\n * 'bubble' \u5934\u9876\u6C14\u6CE1 (\u8BF4\u8BDD\u6587\u672C / \u72B6\u6001)\n * 'outfit' \u6362\u88C5\u6309\u94AE } \u5916\u89C2/\u6587\u6848/\u4EA4\u4E92\u4E0E\u4E3B\u7AD9 TopHeader \u4E00\u81F4; \u70B9\u6309\u94AE\u8D70\u548C setOutfit() / setScene() \u540C\u4E00\u6761\u767D\u540D\u5355 + \u4E32\u884C (last-wins) \u8DEF\u5F84,\n * 'lang' \u8BED\u8A00\u5207\u6362\u6309\u94AE (\u4E2D\u6587 / English / \u65E5\u672C\u8A9E; \u4E0E\u4E3B\u7AD9 TopHeader \u540C\u4E00\u4E2A\u7EC4\u4EF6, \u7528\u6237\u7684\u9009\u62E9\u8BB0\u5728 iframe \u81EA\u5DF1\u7684 localStorage, \u53D1 lang-changed \u4E8B\u4EF6)\n * 'github' GitHub \u6309\u94AE (\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u9879\u76EE\u4ED3\u5E93)\n * 'scene' \u6362\u573A\u666F\u6309\u94AE } \u7167\u5E38\u89E6\u53D1 outfit-changed / scene-changed, \u5BBF\u4E3B\u7528 SDK \u6362\u88C5\u65F6\u6309\u94AE\u72B6\u6001\u540C\u6B65\u3002\u504F\u597D\u7531 iframe \u81EA\u5DF1\u7684 localStorage \u8BB0\u4F4F (\u663E\u5F0F outfit/scene \u4F18\u5148); \u60F3\u81EA\u5DF1\u5B58\u4E5F\u53EF\u7528 persist \u6216\u76D1\u542C\u4E8B\u4EF6\u3002\n * \u4F8B: `ui: ['outfit', 'scene']`\u3002\u672A\u77E5\u540D\u5B57\u5FFD\u7565\u5E76 console.warn\u3002\u521B\u5EFA\u671F\u9009\u9879: \u53D8\u5316\u4F1A\u91CD\u5EFA iframe\u3002\n * 'outfit' / 'scene' / 'lang' / 'github' \u53EA\u5BF9\u65B0\u7248 /embed \u6709\u6548 (\u65E7\u7248\u5FFD\u7565\u672A\u77E5\u90E8\u4EF6\u540D)\u3002\n * @deprecated \u5E03\u5C14\u5199\u6CD5: `ui: true` (0.1.14 \u7684\u65E7\u5199\u6CD5) \u7B49\u4EF7\u4E8E `['chat', 'bubble']` \u5E76 console.warn; \u4E0D\u8981\u518D\u7528\u3002\n */\n ui?: XcUiPart[] | boolean;\n /**\n * \u5185\u7F6E\u754C\u9762 (ui \u91CC\u7684\u6309\u94AE + \u804A\u5929\u680F chat) \u7684\u663E\u793A\u7B56\u7565, \u9ED8\u8BA4 **'transparent'**, \u4E0E Tauri \u684C\u9762\u7AEF\u9010\u5B57\u4E00\u81F4: \u53EA\u6709\u900F\u660E\u573A\u666F\u9ED8\u8BA4\u9690\u85CF, \u5355\u51FB\u89D2\u8272\u51FA\u73B0\n * (\u6309\u4F4F\u62D6\u52A8 / \u957F\u6309\u62D6\u4E0D\u7B97\u70B9\u51FB, \u6240\u4EE5\u4E0D\u4E0E `draggable` \u51B2\u7A81), \u518D\u5355\u51FB\u89D2\u8272\u6216\u5355\u51FB\u7A7A\u767D\u6536\u8D77, 10 \u79D2\u65E0\u64CD\u4F5C\u81EA\u52A8\u6536\u8D77 (\u60AC\u505C\u5728\u6309\u94AE / \u804A\u5929\u680F\u4E0A\u3001\u83DC\u5355\u6253\u5F00\u3001\u8F93\u5165\u4E2D\u4E0D\u6536); \u89E6\u5C4F\u540C\u6837\u662F\u8F7B\u70B9\u89D2\u8272\u3002\u4EAE / \u6697\u573A\u666F\u5E38\u663E\u3002\n * 'transparent' \u9ED8\u8BA4: \u53EA\u6709\u900F\u660E\u573A\u666F\u70B9\u51FB\u51FA\u73B0\n * true \u6240\u6709\u573A\u666F\u90FD\u70B9\u51FB\u51FA\u73B0 (\u4EAE / \u6697\u573A\u666F\u4E5F\u9ED8\u8BA4\u9690\u85CF)\n * false \u4E00\u76F4\u663E\u793A (0.1.15 \u53CA\u4EE5\u524D\u7684\u884C\u4E3A)\n * \u521B\u5EFA\u671F\u9009\u9879 (\u53D8\u5316\u4F1A\u91CD\u5EFA iframe); \u8FD0\u884C\u4E2D\u53EF\u7528 `setConfig({ uiAutoHide })`\u3002\u65E7\u7248 /embed (\u63E1\u624B\u91CC\u6CA1\u6709 capabilities.ui) \u5FFD\u7565\u5B83, \u754C\u9762\u5E38\u663E\u3002\n * \u6CE8\u610F: \u900F\u660E\u573A\u666F\u4E0B\u70B9\u51FB\u5BBF\u4E3B\u9875\u7684\u7A7A\u767D\u5904\u5230\u4E0D\u4E86 iframe (\u7A7F\u900F), \u6240\u4EE5\u90A3\u91CC\u53EA\u80FD\u9760 10 \u79D2\u8D85\u65F6 / \u518D\u70B9\u89D2\u8272\u6536\u8D77\u3002\n */\n uiAutoHide?: XcUiAutoHide;\n /**\n * \u76F8\u673A: `{ fov, distance, height, intro }` (\u89C1 XcCamera; \u8303\u56F4\u89C1 XC_CAMERA_RANGES: fov 15\u201360\u00B0\u3001distance 1\u201315m\u3001height \u00B11m, \u8D8A\u754C\u5939\u5230\u8FB9\u754C), \u7F3A\u7701 = \u4E0D\u8986\u76D6\u3002\n * \u521B\u5EFA\u671F\u901A\u8FC7 URL \u4F20\u7ED9 iframe (\u6A21\u578B\u52A0\u8F7D\u524D\u751F\u6548, \u9996\u6B21\u53D6\u666F / \u63A8\u955C\u5934\u76F4\u63A5\u6309\u5B83\u6765); \u8FD0\u884C\u4E2D\u7528 `setConfig({ camera })` \u6539\n * (\u7F3A\u7701\u952E\u4E0D\u53D8, null = \u6062\u590D\u9ED8\u8BA4; \u4F1A\u7ACB\u523B\u91CD\u65B0\u53D6\u666F\u5E76\u53D6\u6D88\u8FDB\u884C\u4E2D\u7684\u63A8\u955C\u5934)\u3002\u65E7\u7248 /embed (\u63E1\u624B\u91CC\u6CA1\u6709 capabilities.camera) \u5FFD\u7565\u5B83\u3002\n */\n camera?: XcCamera;\n heavy?: XcHeavyMode;\n /**\n * \u81EA\u52A8\u9884\u53D6\u670D\u88C5\u8D44\u6E90\u5230 iframe \u7684 IndexedDB (\u4E4B\u540E setOutfit \u4E0D\u8D70\u7F51\u7EDC), \u9ED8\u8BA4 false\u3002\n * true = \u5168\u90E8\u5185\u7F6E\u670D\u88C5 **\u9664\u4E86**\u5A5A\u7EB1 (13.9MB); string[] = \u53EA\u9884\u53D6\u8FD9\u4E9B id\u3002\n * \u53EA\u5728 `heavy: 'eager'` \u65F6\u81EA\u52A8\u89E6\u53D1 (\u6A21\u578B\u9996\u6B21\u52A0\u8F7D\u5B8C\u6210\u540E\u53D1\u4E00\u6B21 xc.prefetch): \u5E76\u53D1 1, \u6392\u5728 EMAGE \u52A0\u8F7D\u4E4B\u540E, \u4E0D\u4F1A\u548C\u6362\u88C5\u62A2\u5E26\u5BBD\u3002\n * heavy \u4E3A\u9ED8\u8BA4 'lazy' \u65F6\u672C\u9009\u9879\u4E0D\u751F\u6548 \u2014\u2014 \u60F3\u6309\u9700\u9884\u53D6\u8BF7\u76F4\u63A5\u8C03\u7528 `prefetch()`\u3002\n */\n prefetch?: boolean | string[];\n /**\n * iframe \u5185\u6EDA\u8F6E\u7F29\u653E, \u9ED8\u8BA4 true (\u4E0E\u4E3B\u7AD9\u4E00\u81F4)\u3002\u900F\u660E\u573A\u666F\u53EA\u5728\u6307\u9488\u843D\u5728\u89D2\u8272\u4E0A\u65F6\u7F29\u653E, \u5176\u4F59\u4F4D\u7F6E\u6EDA\u8F6E\u7A7F\u900F\u7ED9\u5BBF\u4E3B\u9875;\n * \u4E0D\u900F\u660E\u573A\u666F\u6574\u4E2A iframe \u533A\u57DF\u7684\u6EDA\u8F6E = \u7F29\u653E (\u4F1A\u541E\u6389\u8BE5\u533A\u57DF\u7684\u9875\u9762\u6EDA\u52A8)\u3002\u4F20 false \u9501\u5B9A (URL controls=0)\u3002\n */\n controls?: boolean;\n /** \u89C6\u53E3\u5916\u81EA\u52A8 xc.pause / \u56DE\u6765 xc.resume, \u9ED8\u8BA4 true\u3002 */\n autoPause?: boolean;\n /** \u900F\u660E\u6A21\u5F0F\u4E0B\u6309\"\u662F\u5426\u70B9\u5728\u89D2\u8272\u4E0A\"\u5207\u6362 iframe \u7684 pointer-events, \u9ED8\u8BA4 = transparent\u3002 */\n passthrough?: boolean;\n /** iframe sandbox \u5C5E\u6027; false = \u4E0D\u52A0\u3002\u9ED8\u8BA4\u5141\u8BB8 scripts/same-origin/popups\u3002 */\n sandbox?: string | false;\n /** \u63E1\u624B\u8D85\u65F6 (ms), \u8D85\u65F6 emit error{code:'timeout'}\u3002\u8C03\u5927\u9002\u5408\u6162\u7F51\u7EDC, \u8C03\u5C0F\u53CD\u9988\u66F4\u5FEB\u3002\u9ED8\u8BA4 20000\u3002 */\n handshakeTimeout?: number;\n /**\n * \u7ED9 iframe \u7684 `allow` \u8FFD\u52A0 `cross-origin-isolated` (\u9ED8\u8BA4 false: allow \u4ECD\u662F 'microphone; autoplay')\u3002\n * \u4EC5\u5F53\u5BBF\u4E3B\u9875\u81EA\u5DF1\u5DF2\u8DE8\u6E90\u9694\u79BB (\u54CD\u5E94\u5934 COOP: same-origin + COEP: credentialless / require-corp) \u65F6\u624D\u751F\u6548;\n * \u5F00\u542F\u540E iframe \u5185 crossOriginIsolated=true \u2192 EMAGE \u7684 onnxruntime-web \u53EF\u7528\u591A\u7EBF\u7A0B wasm (SharedArrayBuffer)\u3002\n * \u526F\u4F5C\u7528: \u5BBF\u4E3B\u9875\u4E0A\u7684\u7B2C\u4E09\u65B9\u8D44\u6E90/iframe \u4E5F\u5FC5\u987B\u6EE1\u8DB3 COEP, \u89C1 docs/EMBED.md\u3002\u5BBF\u4E3B\u672A\u9694\u79BB\u65F6\u5F00\u4E86\u4E5F\u65E0\u5BB3 (\u6D4F\u89C8\u5668\u5FFD\u7565)\u3002\n */\n crossOriginIsolated?: boolean;\n zIndex?: number;\n}\n\n/** speakAudio / speakAudioStream \u7684\u9009\u9879\u3002 */\nexport interface XiaochunAudioOptions extends XcAudioOptions {\n /** \u7F16\u7801; \u7701\u7565 = 'encoded' (\u5BB9\u5668\u683C\u5F0F)\u3002\u4F20 ArrayBuffer \u7684\u539F\u59CB PCM \u65F6\u5FC5\u987B\u5199 'pcm16' | 'float32'\u3002 */\n format?: XcAudioFormat;\n /** MIME \u63D0\u793A (\u5982 'audio/mpeg'); Blob \u4F1A\u81EA\u52A8\u53D6 blob.type\u3002 */\n mimeType?: string;\n /** \u539F\u59CB PCM \u7684\u91C7\u6837\u7387 (8000~96000, \u6574\u6BB5\u9ED8\u8BA4 16000, \u6D41\u5F0F\u5FC5\u586B); \u5BB9\u5668\u683C\u5F0F\u5FFD\u7565\u3002 */\n sampleRate?: number;\n /** \u539F\u59CB PCM \u58F0\u9053\u6570 (\u4EA4\u9519), 1 \u6216 2\u3002 */\n channels?: 1 | 2;\n /**\n * ArrayBuffer \u662F\u5426\u7528 transferable \u96F6\u62F7\u8D1D\u53D1\u9001 (\u9ED8\u8BA4 true)\u3002\n * true: \u53D1\u51FA\u540E\u4F20\u5165\u7684\u90A3\u4E2A ArrayBuffer \u4F1A\u88AB detach (byteLength \u53D8 0), \u9002\u5408\u5927\u97F3\u9891;\n * false: \u7ED3\u6784\u5316\u514B\u9686\u4E00\u4EFD, \u539F buffer \u4ECD\u53EF\u7528, \u4F46\u591A\u4E00\u6B21\u62F7\u8D1D\u3002TypedArray \u89C6\u56FE\u603B\u662F\u62F7\u8D1D\u5176\u8303\u56F4\u540E\u518D\u8F6C\u79FB\u3002\n */\n transfer?: boolean;\n /** URL \u5B57\u7B26\u4E32\u7684\u83B7\u53D6\u4F4D\u7F6E: 'host' (\u9ED8\u8BA4) = \u5BBF\u4E3B\u9875 fetch \u540E\u8F6C\u79FB\u7ED9 iframe, \u4E0D\u9700\u8981 iframe \u80FD\u8BBF\u95EE\u8BE5 URL; 'frame' = \u628A URL \u4EA4\u7ED9 iframe fetch (\u9700 https + CORS)\u3002 */\n fetch?: 'host' | 'frame';\n /** \u89E6\u53D1\u540E\u7ACB\u5373\u6253\u65AD\u8FD9\u6B21\u8BF4\u8BDD (\u53D1 xc.audio.end{abort:true})\u3002 */\n signal?: AbortSignal;\n}\n\nexport type XiaochunAudioSource = ArrayBuffer | ArrayBufferView | Blob | string;\n\n/** \u6D41\u5F0F\u97F3\u9891\u53E5\u67C4: write \u539F\u59CB PCM \u5757, end \u6536\u5C3E, abort \u7ACB\u5373\u6253\u65AD; done \u5728\u8BF4\u8BDD\u7ED3\u675F (\u6216\u88AB\u62A2\u5360) \u65F6 resolve\u3002 */\nexport interface XiaochunAudioStream {\n write(data: Int16Array | Float32Array | ArrayBuffer): void;\n end(): void;\n abort(): void;\n readonly done: Promise<void>;\n}\n\nexport type XiaochunErrorCode = XcErrorCode | 'timeout';\nexport interface XiaochunError { code: XiaochunErrorCode; message: string; command?: string }\n\n/** `move` / `resize` \u4E8B\u4EF6\u8F7D\u8377: \u624B\u52BF\u5404\u9636\u6BB5\u5916\u58F3\u5728\u89C6\u53E3\u4E2D\u7684\u4F4D\u7F6E\u4E0E\u5C3A\u5BF8 (CSS px)\u3002 */\nexport interface XiaochunBoxEvent { phase: 'start' | 'move' | 'end'; left: number; top: number; width: number; height: number }\n\nexport interface XiaochunEvents {\n /** xc.ready \u63E1\u624B\u6210\u529F (\u534F\u8BAE\u5C42)\u3002 */\n handshake: XcReadyPayload;\n /** \u6A21\u578B\u52A0\u8F7D\u5B8C\u6210 (= xc.loaded)\u3002 */\n ready: { model: string };\n progress: XcLoadProgressPayload;\n state: XcStatePayload;\n stt: XcSttPayload;\n utterance: XcUtterancePayload;\n 'hit-region': XcHitRegionPayload;\n /** \u670D\u88C5\u5DF2\u751F\u6548 (\u542B\u9996\u6B21\u52A0\u8F7D, initial:true)\u3002\u540C\u76EE\u6807\u7684\u91CD\u590D\u8BF7\u6C42\u4E0D\u4F1A\u518D\u89E6\u53D1\u3002 */\n 'outfit-changed': XcOutfitChangedPayload;\n /** \u573A\u666F\u5DF2\u751F\u6548 (\u542B\u63E1\u624B\u540E\u4E0A\u62A5\u7684\u5F53\u524D\u573A\u666F, initial:true)\u3002 */\n 'scene-changed': XcSceneChangedPayload;\n /** iframe \u754C\u9762\u8BED\u8A00\u5DF2\u751F\u6548 (\u542B\u63E1\u624B\u540E\u4E0A\u62A5\u7684\u5F53\u524D\u8BED\u8A00, initial:true; \u7528\u6237\u70B9\u8BED\u8A00\u6309\u94AE / setConfig({ lang }) \u5F15\u8D77\u7684\u53D8\u5316)\u3002 */\n 'lang-changed': XcLangChangedPayload;\n /** \u7528\u6237\u62D6\u52A8\u5934\u50CF (draggable): start / move / end \u5404\u53D1\u4E00\u6B21, \u4F4D\u7F6E\u5DF2\u9650\u5E45\u3002 */\n move: XiaochunBoxEvent;\n /** \u7528\u6237\u7F29\u653E\u5934\u50CF (resizable): start / move / end \u5404\u53D1\u4E00\u6B21, \u5C3A\u5BF8\u5DF2\u9650\u5E45\u3002 */\n resize: XiaochunBoxEvent;\n error: XiaochunError;\n destroy: undefined;\n}\n\nexport interface XiaochunInstance {\n readonly element: HTMLElement;\n readonly iframe: HTMLIFrameElement | null;\n /** \u6A21\u578B\u52A0\u8F7D\u5B8C\u6210\u65F6 resolve; destroy \u65F6 reject\u3002 */\n readonly ready: Promise<void>;\n /** \u624B\u52A8\u521B\u5EFA iframe (lazy \u6A21\u5F0F\u4E0B\u7B49\u4EF7\u4E8E\"\u7528\u6237\u70B9\u51FB\")\u3002 */\n activate(): void;\n /**\n * \u5FF5\u4E00\u6BB5\u6587\u5B57\u3002`speak`\uFF08\u9ED8\u8BA4\uFF09\u8D70 TTS\uFF0C\u957F\u6587\u5728 iframe \u91CC\u6309\u53E5\u5207\u5F00\uFF0C\u6CA1\u6709\u5B57\u6570\u4E0A\u9650\u3002\n * \u65B0\u7684\u4E00\u6B21\u4F1A\u5148\u628A\u6B63\u5728\u5FF5\u7684\u6DE1\u51FA\uFF08\u5927\u7EA6 0.4 \u79D2\uFF1B\u589E\u76CA\u5DF2\u7ECF\u662F 0 \u5219\u4E0D\u7B49\uFF09\uFF0C\u518D\u63A5\u4E0A\u8FD9\u4E00\u53E5\u3002\u88AB\u6DE1\u51FA\u7684\u90A3\u6B21 Promise \u4F1A resolve\u3002\n * \u663E\u5F0F stop \u7ACB\u523B\u5207\u65AD\uFF0C\u4E0D\u6DE1\u51FA\u3002\n */\n say(text: string, opts?: { mode?: 'speak' | 'chat' }): Promise<void>;\n /**\n * \u76F4\u63A5\u64AD\u653E\u5BBF\u4E3B\u7ED9\u7684\u97F3\u9891 (\u4E0D\u7ECF\u8FC7 TTS): iframe \u5185\u89E3\u7801 \u2192 EMAGE \u751F\u6210\u52A8\u4F5C + \u53E3\u578B, \u5FF5\u5B8C (xc.utterance end) \u624D resolve\u3002\n * \u9996\u6B21\u8C03\u7528\u624D\u52A0\u8F7D EMAGE \u6A21\u578B (\u9996\u6B21\u4F1A\u6162); opts.motion=false \u5219\u53EA\u64AD\u653E\u4E0D\u52A0\u8F7D\u3002\n * xc.utterance start \u5728\u601D\u8003\u52A8\u4F5C\u548C\u9996\u7A97\u4E4B\u540E\u3001\u58F0\u97F3\u65F6\u949F\u8D77\u6B65\u65F6\u624D\u53D1, \u4E0D\u662F\u547D\u4EE4\u4E00\u5230\u5C31\u53D1\u3002\n * opts.playbackRate (0.25~3, \u9ED8\u8BA4 1) \u4E0E opts.volume (0~1, \u9ED8\u8BA4 1) \u53EA\u4F5C\u7528\u4E8E\u8FD9\u6B21\u5BBF\u4E3B\u97F3\u9891, \u52A8\u4F5C\u65F6\u949F\u4E00\u8D77\u53D8\u3002\n * \u4E0D\u5199\u5219\u6CBF\u7528\u4E0A\u4E00\u6B21 setPlaybackRate / setVolume\u3002audible:false \u65F6\u97F3\u91CF\u4E0D\u4F1A\u9001\u5230\u626C\u58F0\u5668\u3002TTS (say) \u4E0D\u53D7\u5F71\u54CD\u3002\n * \u65B0\u7684 say / speakAudio \u4F1A\u5148\u6DE1\u51FA\u8FD9\u4E00\u53E5\uFF08\u589E\u76CA\u5DF2\u7ECF\u662F 0 \u5219\u4E0D\u7B49\uFF09\u3002AbortSignal \u7ACB\u523B\u5207\u65AD\u3002\n */\n speakAudio(source: XiaochunAudioSource, opts?: XiaochunAudioOptions): Promise<void>;\n /** \u6D41\u5F0F\u97F3\u9891 (\u539F\u59CB PCM \u5206\u5757)\u3002sampleRate \u5FC5\u586B; \u9996\u4E2A write \u5F00\u59CB\u8FD9\u6B21\u8BF4\u8BDD, utterance start \u4ECD\u7B49\u5230\u58F0\u97F3\u65F6\u949F\u8D77\u6B65\u3002 */\n speakAudioStream(opts: XiaochunAudioOptions & { sampleRate: number }): XiaochunAudioStream;\n motion(m: XcMotionPayload | string): Promise<void>;\n expression(name: XcExpressionPayload['name']): Promise<void>;\n /** TODO: \u534F\u8BAE\u5DF2\u9884\u7559, /embed \u6682\u672A\u5B9E\u73B0 \u2192 \u4F1A\u6536\u5230 error{code:'unsupported'}\u3002 */\n lookAt(x: number, y: number): Promise<void>;\n /**\n * \u6362\u5185\u7F6E\u670D\u88C5 (id \u89C1 getOutfits())\u3002Promise \u5728\u670D\u88C5**\u751F\u6548\u540E** resolve (xc.outfit-changed)\u3002\n * \u5E76\u53D1\u89C4\u5219: \u4E32\u884C + last-wins \u2014\u2014 \u52A0\u8F7D\u4E2D\u518D\u6B21\u8C03\u7528\u4F1A\u6392\u961F, \u6392\u961F\u7684\u65E7\u8BF7\u6C42\u88AB\u66F4\u65B0\u7684\u9876\u6389\u65F6 reject `[busy]` (\u53EF\u5FFD\u7565, \u4EE5\u6700\u65B0\u4E00\u6B21\u4E3A\u51C6);\n * \u8BF4\u8BDD\u4E0D\u4F1A\u88AB\u6253\u65AD, \u6362\u88C5\u5728\u540E\u53F0\u5B8C\u6210\u540E\u624D\u751F\u6548\u3002\u76EE\u6807\u5DF2\u662F\u5F53\u524D\u670D\u88C5\u5219\u76F4\u63A5 resolve\u3002\n * \u65E7\u7248 /embed (\u63E1\u624B\u91CC\u6CA1\u6709 capabilities.outfits) \u4F1A reject `[unsupported]`\u3002\n */\n setOutfit(id: string): Promise<void>;\n /** \u5207\u573A\u666F ('light' | 'dark' | 'transparent' | 'beach3d', \u89C1 getScenes())\u3002SDK \u540C\u6B65\u5916\u58F3\u80CC\u666F\u3001\u7A7F\u900F\u5F00\u5173\u4E0E pointer-events\u3002 */\n setScene(id: XcSceneId | (string & {})): Promise<void>;\n /**\n * \u9884\u53D6\u5185\u7F6E\u670D\u88C5\u8D44\u6E90\u5230 iframe \u7684 IndexedDB (\u53EA\u4E0B\u8F7D, \u4E0D\u89E3\u538B\u4E0D\u5408\u6210)\u3002ids \u7701\u7565 = \u5168\u90E8\u5185\u7F6E\u670D\u88C5, \u5A5A\u7EB1 (13.9MB) \u9664\u5916; \u663E\u5F0F\u70B9\u540D\u5219\u7167\u505A\u3002\n * \u5168\u5C40\u4E32\u884C (\u5E76\u53D1 1); resolve \u7684\u7ED3\u679C\u91CC failed \u975E\u7A7A\u65F6\u53EF\u7A0D\u540E\u91CD\u8BD5\u3002\u8FDB\u5EA6\u89C1 progress \u4E8B\u4EF6 (payload.phase === 'prefetch')\u3002\n * \u65E7\u7248 /embed reject `[unsupported]`; \u7528\u6237\u5F00\u4E86\u7701\u6D41\u91CF\u6A21\u5F0F\u65F6 resolve \u4E14 skipped:'save-data'\u3002\n */\n prefetch(ids?: string[]): Promise<XcPrefetchedPayload>;\n /** \u63E1\u624B\u540E\u8FD4\u56DE\u53EF\u6362\u7684\u5185\u7F6E\u670D\u88C5 (\u4E0D\u542B\u88F8\u6A21); \u65E7\u7248 /embed \u8FD4\u56DE []\u3002\u4F1A\u89E6\u53D1 iframe \u521B\u5EFA (lazy \u6A21\u5F0F)\u3002 */\n getOutfits(): Promise<XcOutfitInfo[]>;\n /** \u63E1\u624B\u540E\u8FD4\u56DE\u53EF\u5207\u7684\u573A\u666F; \u65E7\u7248 /embed \u8FD4\u56DE []\u3002 */\n getScenes(): Promise<XcSceneInfo[]>;\n /** \u6700\u8FD1\u4E00\u6B21 xc.outfit-changed \u7684\u670D\u88C5 id (\u81EA\u5B9A\u4E49 URL \u6A21\u578B / \u5C1A\u672A\u52A0\u8F7D = null)\u3002 */\n readonly outfit: string | null;\n /** \u6700\u8FD1\u4E00\u6B21 xc.scene-changed \u7684\u573A\u666F id (\u63E1\u624B\u524D = null)\u3002 */\n readonly scene: string | null;\n /**\n * \u65E7\u547D\u4EE4, \u4FDD\u7559\u517C\u5BB9\u3002\u5185\u7F6E\u670D\u88C5\u8BF7\u6539\u7528 `setOutfit` (\u5E76\u53D1\u66F4\u7A33\u3001\u80FD\u62FF\u5230\u5B8C\u6210\u901A\u77E5)\u3002\n * `{ url }` \u9ED8\u8BA4\u88AB\u62D2\u7EDD (reject `[unsupported]`), \u9700\u521B\u5EFA\u9009\u9879 `allowCustomModel: true`\u3002\n */\n setModel(m: string | { url?: string; outfit?: string; name?: string }): Promise<void>;\n setConfig(cfg: XcConfig): Promise<void>;\n /** \u8FD0\u884C\u65F6\u8BBE\u7F6E\u5916\u58F3\u5C3A\u5BF8 (\u6570\u5B57 = px, \u5B57\u7B26\u4E32 = CSS \u957F\u5EA6, undefined = \u6062\u590D\u9ED8\u8BA4 600x1080 (\u53D7\u89C6\u53E3\u9650\u5236)); \u4E0D\u91CD\u5EFA iframe\u3002\u7528\u6237\u7F29\u653E\u540E\u5BBF\u4E3B\u8981\u540C\u6B65\u81EA\u5DF1\u7684\u72B6\u6001\u65F6\u7528 `resize` \u4E8B\u4EF6\u3002 */\n setSize(width: number | string | undefined, height: number | string | undefined): void;\n /** \u5916\u58F3\u5F53\u524D\u5728\u89C6\u53E3\u4E2D\u7684\u4F4D\u7F6E\u4E0E\u5C3A\u5BF8 (CSS px)\u3002 */\n getBox(): { left: number; top: number; width: number; height: number };\n /** \u6E05\u9664 `persistBox` \u4FDD\u5B58\u7684\u4F4D\u7F6E / \u5927\u5C0F\uFF08\u6CA1\u5F00 persistBox \u65F6\u662F\u7A7A\u64CD\u4F5C\uFF09\u3002`reset: true` \u53E6\u5916\u628A\u5F53\u524D\u76D2\u5B50\u8FD8\u539F\u5230\u521D\u59CB\u4F4D\u7F6E\u548C\u5927\u5C0F\uFF08\u6309 width / height \u9009\u9879\u6216\u9ED8\u8BA4\u503C\uFF0C\u4E0D\u91CD\u5EFA iframe\uFF09\u3002 */\n clearPersistedBox(opts?: { reset?: boolean }): void;\n /** \u8FD0\u884C\u65F6\u5F00\u5173\u624B\u52BF\u62D6\u52A8 (\u7B49\u4EF7\u4E8E `draggable` \u9009\u9879), \u4E0D\u91CD\u5EFA iframe\u3002 */\n /** \u6539\u5916\u58F3\u5706\u89D2 (\u6570\u5B57 px / CSS \u957F\u5EA6); \u4F20 undefined \u6062\u590D\u9ED8\u8BA4 (\u975E\u900F\u660E 20px / \u900F\u660E 0)\u3002 */\n setBorderRadius(radius: number | string | undefined): void;\n setDraggable(on: boolean): void;\n /** \u8FD0\u884C\u65F6\u5F00\u5173 / \u8C03\u6574\u89D2\u843D\u7F29\u653E (\u7B49\u4EF7\u4E8E `resizable` \u9009\u9879), \u4E0D\u91CD\u5EFA iframe\u3002false = \u5173\u3002 */\n setResizable(on: boolean | XiaochunResizeLimits): void;\n startListening(): Promise<void>;\n stopListening(): Promise<void>;\n mic(enabled: boolean): Promise<void>;\n pause(): void;\n resume(): void;\n /**\n * \u6539\u6B63\u5728\u64AD\u7684\u5BBF\u4E3B\u97F3\u9891\u500D\u901F (0.25~3, \u8D8A\u754C\u7531 iframe \u5939\u5230\u8303\u56F4\u5185)\u3002\u4E0D\u91CD\u5F00\u8FD9\u6B21\u8BF4\u8BDD, \u64AD\u653E\u5934\u4E0D\u8DF3\u3002\n * \u6CA1\u5728\u64AD\u65F6\u5148\u8BB0\u4E0B, \u4E0B\u4E00\u6B21 speakAudio \u7528\u3002\u4E0D\u5F71\u54CD TTS\u3002\n */\n setPlaybackRate(rate: number): void;\n /** \u6539\u5BBF\u4E3B\u97F3\u9891\u97F3\u91CF (0~1)\u3002audible:false \u65F6\u626C\u58F0\u5668\u4ECD\u662F 0, \u8FD9\u4E2A\u503C\u7559\u5230\u4E0B\u6B21\u51FA\u58F0\u3002 */\n setVolume(volume: number): void;\n destroy(): void;\n on<K extends keyof XiaochunEvents>(event: K, cb: (payload: XiaochunEvents[K]) => void): () => void;\n}\n\nconst FALLBACK_PLACEHOLDER =\n 'data:image/svg+xml;utf8,' +\n encodeURIComponent(\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 120 180\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"#f6b8ae\"/><stop offset=\"1\" stop-color=\"#ea8377\"/></linearGradient></defs><ellipse cx=\"60\" cy=\"170\" rx=\"34\" ry=\"6\" fill=\"#000\" opacity=\".12\"/><circle cx=\"60\" cy=\"48\" r=\"26\" fill=\"url(#g)\" opacity=\".75\"/><path d=\"M26 160c2-40 18-62 34-62s32 22 34 62z\" fill=\"url(#g)\" opacity=\".6\"/></svg>',\n );\n\n/**\n * \u4E0D\u8BA9\u5934\u50CF\u88AB\u5BBF\u4E3B\u9875\u7684\u6587\u5B57\u9009\u533A\u9009\u4E2D: Chrome \u5728\u9009\u533A\u8DE8\u8FC7 iframe (\u62D6\u9009\u5212\u8FC7 / Cmd\u00B7Ctrl+A / \u5168\u9009) \u65F6\u4F1A\u7ED9\u6574\u5757 iframe \u76D6\u4E00\u5C42\u84DD\u8272\u9AD8\u4EAE\n * (\u5B9E\u6D4B Chrome \u7684 iframe \u4F1A\u88AB\u67D3\u8272; \u7ED9 iframe \u81EA\u8EAB\u8BBE user-select:none \u5373\u53EF\u6D88\u9664, \u5BBF\u4E3B\u9875\u81EA\u5DF1\u7684\u6587\u5B57\u4ECD\u7167\u5E38\u88AB\u9009\u4E2D)\u3002\n * \u53EA\u7528 CSSOM \u5185\u8054\u6837\u5F0F (\u4E0D\u6CE8\u5165 <style>), \u4E0D\u53D7\u5BBF\u4E3B CSP style-src \u5F71\u54CD, \u4E5F\u4E0D\u5F71\u54CD pointer-events / \u900F\u660E\u7A7F\u900F\u3002\n */\nconst noSelect = (st: CSSStyleDeclaration) => {\n st.setProperty('user-select', 'none');\n st.setProperty('-webkit-user-select', 'none');\n st.setProperty('-webkit-touch-callout', 'none');\n st.setProperty('-webkit-tap-highlight-color', 'transparent');\n};\n\nconst css = (v: number | string | undefined, d: string) =>\n v === undefined ? d : typeof v === 'number' ? `${v}px` : v;\n\n/** \u9ED8\u8BA4\u5C3A\u5BF8 (px): 600x1080\u3002\u53EA\u5728\u6CA1\u663E\u5F0F\u4F20 width / height \u65F6\u4F7F\u7528, \u5E76\u53D7\u89C6\u53E3\u9650\u5236 (\u89C1 defaultWidthCss / defaultHeightCss)\u3002 */\nexport const XC_DEFAULT_WIDTH = 600;\nexport const XC_DEFAULT_HEIGHT = 1080;\n/**\n * \u9ED8\u8BA4\u5BBD: 600px \u4F46\u4E0D\u8D85\u8FC7\u89C6\u53E3\u5BBD (min(600px, 100vw))\u3002\u4E0D\u7528 min(600px, 100%): \u767E\u5206\u6BD4\u5728 \"\u6309\u5185\u5BB9\u6536\u7F29\" \u7684\u5BB9\u5668 (inline-block / \u7EDD\u5BF9\u5B9A\u4F4D / <xiaochun-avatar> \u7684 :host)\n * \u91CC\u4F1A\u5FAA\u73AF\u4F9D\u8D56\u800C\u584C\u6210 0\u3002\u6BD4\u89C6\u53E3\u66F4\u7A84\u7684\u5BB9\u5668 (\u5982 300px \u7684\u4FA7\u680F) \u7531\u5916\u58F3\u7684 max-width:100% \u518D\u94B3\u4E00\u6B21, \u6240\u4EE5\u7A84\u5C4F / \u7A84\u5BB9\u5668\u90FD\u4E0D\u4F1A\u6EA2\u51FA\u3002\n */\nexport const defaultWidthCss = (floating = false): string =>\n floating ? `min(${XC_DEFAULT_WIDTH}px, calc(100vw - 2 * var(--xc-offset-x, 16px)))` : `min(${XC_DEFAULT_WIDTH}px, 100vw)`;\n/**\n * \u9ED8\u8BA4\u9AD8: 1080px \u4F46\u4E0D\u8D85\u8FC7\u89C6\u53E3\u9AD8 (\u5C0F\u7A97\u53E3 / \u624B\u673A\u6A2A\u5C4F\u94B3\u5236)\u3002\u6D4F\u89C8\u5668\u652F\u6301\u65F6\u7528 svh (\u79FB\u52A8\u7AEF\u5730\u5740\u680F\u4F38\u7F29\u65F6\u4E5F\u4E0D\u6EA2\u51FA), \u5426\u5219 vh\u3002\n * SSR \u4E0B (\u6CA1\u6709 CSS.supports) \u4E00\u5F8B vh, \u4E0E React \u670D\u52A1\u7AEF\u6E32\u67D3\u7684\u9996\u5E27\u4FDD\u6301\u4E00\u81F4\u3002\n */\nexport const defaultHeightCss = (floating = false): string => {\n const unit = typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('height', '1svh') ? 'svh' : 'vh';\n // \u60AC\u6D6E (position\u2260inline) \u8D34\u7740\u5C4F\u5E55\u8FB9 + \u8FB9\u8DDD (--xc-offset-*), \u9ED8\u8BA4\u503C\u8981\u628A\u4E24\u4FA7\u8FB9\u8DDD\u6263\u6389, \u5426\u5219\u6574\u5757\u4F1A\u9876\u51FA\u5C4F\u5E55\n return floating ? `min(${XC_DEFAULT_HEIGHT}px, calc(100${unit} - 2 * var(--xc-offset-y, 16px)))` : `min(${XC_DEFAULT_HEIGHT}px, 100${unit})`;\n};\n\nexport function createXiaochun(options: XiaochunOptions): XiaochunInstance {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n throw new Error('[project-xiaochun] createXiaochun must run in a browser');\n }\n const container =\n typeof options.container === 'string' ? document.querySelector<HTMLElement>(options.container) : options.container;\n if (!container) throw new Error(`[project-xiaochun] container not found: ${String(options.container)}`);\n\n // \u2500\u2500 \u5730\u5740 / origin \u2500\u2500\n const src = options.src ?? XC_DEFAULT_SRC;\n let srcUrl: URL;\n try { srcUrl = new URL(src, window.location.href); } catch { throw new Error(`[project-xiaochun] invalid src: ${src}`); }\n const frameOrigin = normalizeOrigin(options.origin ?? srcUrl.origin);\n if (!frameOrigin) throw new Error('[project-xiaochun] invalid origin (wildcards are not allowed)');\n const trusted = new Set<string>([frameOrigin]);\n for (const o of options.allowedOrigins ?? []) {\n const n = normalizeOrigin(o);\n if (!n) throw new Error(`[project-xiaochun] invalid allowedOrigins entry: ${o} (wildcards are not allowed)`);\n trusted.add(n);\n }\n\n // \u2500\u2500 \u504F\u597D (persist, \u53EF\u9009): \u5BBF\u4E3B localStorage; iframe \u53E6\u6709\u81EA\u5DF1\u7684\u5B58\u50A8 \u2500\u2500\n const prefsKey = options.persist === 'host' ? 'xiaochun:prefs' : typeof options.persist === 'string' && options.persist ? options.persist : null;\n type Prefs = { outfit?: string; scene?: string };\n const readPrefs = (): Prefs => {\n if (!prefsKey) return {};\n try {\n const raw = JSON.parse(window.localStorage.getItem(prefsKey) ?? 'null') as Record<string, unknown> | null;\n const out: Prefs = {};\n if (raw && typeof raw === 'object') {\n if (isXcId(raw.outfit)) out.outfit = raw.outfit;\n if (isXcId(raw.scene)) out.scene = raw.scene;\n }\n return out;\n } catch { return {}; }\n };\n const writePrefs = (patch: { [K in keyof Prefs]?: string | null }) => {\n if (!prefsKey) return;\n try {\n const next: Record<string, string> = { ...readPrefs() };\n for (const [k, v] of Object.entries(patch)) { if (v) next[k] = v; else delete next[k]; }\n window.localStorage.setItem(prefsKey, JSON.stringify(next));\n } catch { /* localStorage \u4E0D\u53EF\u7528 (\u9690\u79C1\u6A21\u5F0F / \u914D\u989D): \u9759\u9ED8\u653E\u5F03\u4FDD\u5B58 */ }\n };\n const prefs = readPrefs();\n\n if (options.model !== undefined && options.outfit === undefined) {\n console.warn('[project-xiaochun] option \"model\" is deprecated, use \"outfit\" (built-in outfit id).');\n }\n // ui: \u90E8\u4EF6\u540D\u6570\u7EC4 (\u767D\u540D\u5355); \u5E03\u5C14 true \u662F\u5F03\u7528\u7684\u65E7\u5199\u6CD5 (= chat + bubble)\n const uiParse = normalizeXcUiOption(options.ui);\n const uiParts = uiParse.parts;\n // uiAutoHide: \u7F3A\u7701 = \u4E0D\u5199\u8FDB URL (iframe \u7528\u81EA\u5DF1\u7684\u9ED8\u8BA4 true); \u975E\u6CD5\u503C console.warn \u5E76\u5FFD\u7565\n const uiAutoHide = parseXcUiAutoHide(options.uiAutoHide);\n if (options.uiAutoHide !== undefined && uiAutoHide === undefined) console.warn(`[project-xiaochun] ignoring invalid uiAutoHide: ${String(options.uiAutoHide)} (use true | false | 'transparent')`);\n // camera: \u975E\u6CD5 \u2192 console.warn \u5E76\u6574\u4F53\u5FFD\u7565; \u8D8A\u754C \u2192 \u5939\u5230\u8303\u56F4\u5E76 warn; null / undefined \u9879\u4E0D\u5199\u8FDB URL\n let camera: XcCamera = {};\n if (options.camera !== undefined) {\n const r = normalizeXcCamera(options.camera);\n if (r.ok) {\n camera = r.camera;\n if (r.clamped.length) console.warn(`[project-xiaochun] camera.${r.clamped.join(', camera.')} out of range, clamped (fov ${XC_CAMERA_RANGES.fov.join('\u2013')}, distance ${XC_CAMERA_RANGES.distance.join('\u2013')}, height ${XC_CAMERA_RANGES.height.join('\u2013')})`);\n } else console.warn(`[project-xiaochun] ignoring invalid camera option: ${r.error}`);\n }\n if (uiParse.legacy) console.warn('[project-xiaochun] option \"ui: true\" is deprecated, use an array of parts, e.g. ui: [\\'chat\\', \\'bubble\\', \\'outfit\\', \\'scene\\'].');\n if (uiParse.unknown.length) console.warn(`[project-xiaochun] ignoring unknown ui part(s): ${uiParse.unknown.join(', ')} (known: ${XC_UI_PARTS.join(', ')})`);\n const outfitOpt = options.outfit ?? (isXcId(options.model) ? options.model : undefined);\n const initialOutfit = outfitOpt ?? prefs.outfit;\n const initialScene = options.scene ?? prefs.scene ?? (options.transparent ? 'transparent' : undefined);\n // \u573A\u666F\u540D\u672A\u77E5 (\u65E7\u7248/\u81EA\u5B9A\u4E49\u90E8\u7F72) \u65F6\u9000\u56DE transparent \u9009\u9879; \u63E1\u624B\u540E\u7684 xc.scene-changed \u4F1A\u6821\u6B63\n let transparent = initialScene === 'transparent' || (initialScene !== 'light' && initialScene !== 'dark' && initialScene !== 'beach3d' && (options.transparent ?? false));\n let passthrough = options.passthrough ?? transparent;\n const lazy: XiaochunLazy = options.lazy ?? true;\n const position = options.position ?? 'inline';\n const handshakeTimeout = options.handshakeTimeout ?? 20_000;\n\n // \u2500\u2500 DOM: wrapper (\u56FA\u5B9A\u5C3A\u5BF8, \u9632 CLS) \u2500\u2500\n const wrapper = document.createElement('div');\n wrapper.setAttribute('data-xiaochun', '');\n const ws = wrapper.style;\n ws.position = position === 'inline' ? 'relative' : 'fixed';\n ws.width = css(options.width, defaultWidthCss(position !== 'inline'));\n ws.height = css(options.height, defaultHeightCss(position !== 'inline'));\n ws.maxWidth = '100%';\n ws.overflow = 'visible';\n ws.contain = 'layout style';\n noSelect(ws); // \u5BBF\u4E3B\u9875\u7684\u6587\u5B57\u9009\u533A (\u62D6\u9009\u5212\u8FC7 / Cmd+A) \u4E0D\u4F1A\u628A\u5934\u50CF\u67D3\u6210\u84DD\u8272, \u89C1 noSelect\n // \u2500\u2500 \u6837\u5F0F hook: \u5BBF\u4E3B\u7528 CSS \u81EA\u5B9A\u4E49\u5C5E\u6027\u63A7\u5236\u5916\u89C2 (\u81EA\u5B9A\u4E49\u5C5E\u6027\u4F1A\u7A7F\u900F Shadow DOM, \u5728\u5BB9\u5668\u6216\u4EFB\u610F\u7956\u5148\u4E0A\u8BBE\u7F6E\u5373\u53EF) \u2500\u2500\n // \u6CE8\u610F: \u53EA\u80FD\u5F71\u54CD iframe \u7684\u300C\u5916\u58F3\u300D(\u5706\u89D2/\u9634\u5F71/\u4F4D\u7F6E/\u5C42\u7EA7/\u80CC\u666F); iframe \u5185\u90E8 (\u89D2\u8272\u6E32\u67D3\u3001\u6C14\u6CE1) \u662F\u8DE8\u57DF\u6587\u6863, \u5BBF\u4E3B CSS \u78B0\u4E0D\u5230\u3002\n // --xc-radius \u5706\u89D2, CSS \u957F\u5EA6, \u9ED8\u8BA4 0\u3002\u8303\u56F4 0 ~ \u5BBD\u5EA6\u7684\u4E00\u534A (50% = \u5706\u5F62\u5934\u50CF\u6846)\u3002\u8C03\u5927 = \u66F4\u5706; \u8FC7\u5927\u4F1A\u88C1\u6389\u89D2\u8272\u7684\u5934/\u811A\u3002\n // --xc-shadow box-shadow \u7B80\u5199, \u9ED8\u8BA4 none\u3002\u900F\u660E\u60AC\u6D6E\u5934\u50CF\u5EFA\u8BAE\u4FDD\u6301 none (\u5426\u5219\u4F1A\u51FA\u73B0\u4E00\u4E2A\u65B9\u5757\u9634\u5F71); \u5361\u7247\u5F0F\u5D4C\u5165\u53EF\u7528 '0 8px 24px rgba(0,0,0,.18)'\u3002\n // --xc-z-index \u4EC5\u60AC\u6D6E (position\u2260inline) \u751F\u6548, \u9ED8\u8BA4 2147483000 (\u63A5\u8FD1 int \u4E0A\u9650, \u76D6\u4F4F\u7EDD\u5927\u591A\u6570\u9875\u9762\u5143\u7D20)\u3002\u8C03\u5C0F = \u8BA9\u5BBF\u4E3B\u7684\u5F39\u7A97/\u5BFC\u822A\u76D6\u5728\u5934\u50CF\u4E0A\u9762\u3002\n // --xc-offset-x \u4EC5\u60AC\u6D6E: \u8DDD\u5C4F\u5E55\u5DE6/\u53F3\u8FB9\u7684\u8FB9\u8DDD, \u9ED8\u8BA4 16px\u3002\u8C03\u5927 = \u79BB\u8FB9\u7F18\u66F4\u8FDC (\u79FB\u52A8\u7AEF\u6CE8\u610F\u907F\u5F00\u7CFB\u7EDF\u624B\u52BF\u533A, \u5EFA\u8BAE \u2265 12px)\u3002\n // --xc-offset-y \u4EC5\u60AC\u6D6E: \u8DDD\u5C4F\u5E55\u5E95\u8FB9\u7684\u8FB9\u8DDD, \u9ED8\u8BA4 16px\u3002\u8C03\u5927 = \u62AC\u9AD8 (\u907F\u5F00\u5E95\u90E8\u5BFC\u822A\u680F/Cookie \u6A2A\u5E45)\u3002\n // --xc-bg \u975E\u900F\u660E\u6A21\u5F0F\u4E0B iframe \u52A0\u8F7D\u524D/\u5706\u89D2\u5916\u7684\u5E95\u8272, \u9ED8\u8BA4 transparent\u3002transparent: true \u65F6\u5FFD\u7565\u3002\n ws.boxShadow = 'var(--xc-shadow, none)';\n const applyWrapperBg = () => { ws.background = transparent ? '' : 'var(--xc-bg, transparent)'; };\n applyWrapperBg();\n // \u5706\u89D2: \u9009\u9879 > (\u975E\u900F\u660E 20px / \u900F\u660E 0); \u5BBF\u4E3B\u7684 --xc-radius CSS \u53D8\u91CF\u59CB\u7EC8\u4F18\u5148\n let radiusOpt: number | string | undefined = options.borderRadius;\n const radiusValue = () => (radiusOpt !== undefined ? css(radiusOpt, '0px') : transparent ? '0px' : `${XC_WINDOW_CORNER_RADIUS}px`);\n const applyRadius = () => {\n const v = `var(--xc-radius, ${radiusValue()})`;\n ws.borderRadius = v;\n if (placeholderEl) placeholderEl.style.borderRadius = v;\n if (iframe) iframe.style.borderRadius = v;\n };\n ws.borderRadius = `var(--xc-radius, ${radiusValue()})`;\n if (position === 'bottom-right') { ws.right = 'var(--xc-offset-x, 16px)'; ws.bottom = 'var(--xc-offset-y, 16px)'; }\n if (position === 'bottom-left') { ws.left = 'var(--xc-offset-x, 16px)'; ws.bottom = 'var(--xc-offset-y, 16px)'; }\n if (position !== 'inline') ws.zIndex = `var(--xc-z-index, ${options.zIndex ?? 2147483000})`;\n ws.pointerEvents = 'none'; // \u5BB9\u5668\u672C\u8EAB\u4E0D\u5403\u4E8B\u4EF6; \u5360\u4F4D/iframe \u5404\u81EA\u5F00\n wrapper.setAttribute('part', 'wrapper'); // ::part(wrapper) (\u4EC5 <xiaochun-avatar> \u7684 Shadow DOM \u5185\u6709\u610F\u4E49)\n container.appendChild(wrapper);\n\n let placeholderEl: HTMLElement | null = null;\n if (options.placeholder !== false) {\n if (options.placeholder instanceof HTMLElement) placeholderEl = options.placeholder;\n else {\n const img = document.createElement('img');\n img.src = options.placeholder || FALLBACK_PLACEHOLDER;\n img.alt = 'Project XiaoChun';\n img.decoding = 'async';\n img.width = XC_DEFAULT_WIDTH; img.height = XC_DEFAULT_HEIGHT; // \u6709 width/height \u5C5E\u6027 + \u4E0B\u9762\u7684 100% \u6837\u5F0F, \u89E3\u7801\u524D\u4E5F\u4E0D\u6296\u52A8\n placeholderEl = img;\n }\n const ps = placeholderEl.style;\n placeholderEl.setAttribute('part', 'placeholder');\n ps.borderRadius = `var(--xc-radius, ${radiusValue()})`;\n ps.position = 'absolute'; ps.inset = '0'; ps.width = '100%'; ps.height = '100%';\n ps.objectFit = 'contain'; ps.transition = 'opacity .25s ease'; ps.pointerEvents = 'auto';\n ps.cursor = lazy === false ? 'default' : 'pointer';\n noSelect(ps); // \u5360\u4F4D\u56FE (<img>) \u540C\u6837\u4F1A\u88AB\u9009\u533A\u67D3\u84DD\n placeholderEl.addEventListener('click', () => activate());\n wrapper.appendChild(placeholderEl);\n }\n\n // \u2500\u2500 \u4E8B\u4EF6 \u2500\u2500\n const listeners = new Map<string, Set<(p: any) => void>>();\n const emit = <K extends keyof XiaochunEvents>(ev: K, payload: XiaochunEvents[K]) => {\n listeners.get(ev)?.forEach((cb) => { try { cb(payload); } catch (e) { console.error('[project-xiaochun] listener error', e); } });\n };\n\n // \u2500\u2500 ready promise \u2500\u2500\n let resolveReady!: () => void;\n let rejectReady!: (e: Error) => void;\n const ready = new Promise<void>((res, rej) => { resolveReady = res; rejectReady = rej; });\n ready.catch(() => {}); // \u907F\u514D\u6CA1\u4EBA await \u65F6\u7684 unhandled rejection\n\n // \u2500\u2500 \u72B6\u6001 \u2500\u2500\n let iframe: HTMLIFrameElement | null = null;\n let port: MessagePort | null = null;\n let destroyed = false;\n let userPaused = false;\n let autoPaused = false;\n let loaded = false;\n let lastHit = false;\n let caps: XcReadyPayload['capabilities'] | null = null;\n let currentOutfit: string | null = null;\n let currentScene: string | null = null;\n /** \u547D\u4EE4\u4F9D\u8D56\u7684 capabilities \u5B57\u6BB5; \u63E1\u624B\u540E\u82E5 iframe \u6CA1\u6709 (\u65E7\u7248 /embed) \u5C31\u672C\u5730 reject, \u4E0D\u53D1\u51FA\u53BB\u3002 */\n type CapKey = 'outfits' | 'scenes' | 'prefetch';\n const queue: Array<{ type: XcHostMessageType; payload?: unknown; id?: string; transfer?: Transferable[]; requires?: CapKey }> = [];\n const pending = new Map<string, { resolve: (v?: unknown) => void; reject: (e: Error) => void; sawEnd: boolean }>();\n let idSeq = 0;\n let handshakeTimer: ReturnType<typeof setTimeout> | null = null;\n let io: IntersectionObserver | null = null;\n let idleHandle: number | null = null;\n let resolveHandshake!: () => void;\n const handshaked = new Promise<void>((res) => { resolveHandshake = res; });\n\n /** \u672C\u5730\u62D2\u7EDD\u4E00\u6761\u547D\u4EE4 (iframe \u4E0D\u652F\u6301): reject \u5BF9\u5E94 Promise + \u89E6\u53D1 error \u4E8B\u4EF6\u3002 */\n const failLocal = (type: XcHostMessageType, id: string | undefined, code: XiaochunErrorCode, message: string) => {\n emit('error', { code, message, command: type });\n const p = id ? pending.get(id) : undefined;\n if (p && id) { pending.delete(id); p.reject(new Error(`[${code}] ${message}`)); }\n };\n const unsupportedByFrame = (type: XcHostMessageType, id: string | undefined) =>\n failLocal(type, id, 'unsupported', `${type} is not supported by this /embed deployment (older version; its xc.ready has no capabilities for it)`);\n\n const postToFrame = (type: XcHostMessageType, payload?: unknown, id?: string, transfer?: Transferable[], requires?: CapKey) => {\n if (!port) { queue.push({ type, payload, id, transfer, requires }); return; }\n if (requires && !caps?.[requires]) { unsupportedByFrame(type, id); return; }\n port.postMessage(xcMessage(type, payload, id), transfer ?? []);\n };\n\n const send = (type: XcHostMessageType, payload?: unknown, wait = false, transfer?: Transferable[], fixedId?: string, requires?: CapKey): Promise<any> => {\n if (destroyed) return Promise.reject(new Error('[project-xiaochun] instance destroyed'));\n if (!iframe) activate(); // \u4EFB\u4F55\u4EA4\u4E92 API \u90FD\u89C6\u4E3A\"\u7528\u6237\u610F\u56FE\", \u7ACB\u5373\u521B\u5EFA\n const id = fixedId ?? `c${++idSeq}`;\n // \u547D\u4EE4\u5E94\u7B54\u53EF\u4EE5\u5E26\u4E00\u4E2A\u503C (\u5982 xc.prefetched \u7684\u7ED3\u679C); \u5176\u5B83\u547D\u4EE4 resolve(undefined)\n const p: Promise<any> = wait\n ? new Promise<any>((resolve, reject) => pending.set(id, { resolve, reject, sawEnd: false }))\n : Promise.resolve();\n postToFrame(type, payload, id, transfer, requires);\n return p;\n };\n\n // \u2500\u2500 \u5BBF\u4E3B\u97F3\u9891 \u2500\u2500\n /** TypedArray \u89C6\u56FE \u2192 \u53EA\u542B\u5176\u8303\u56F4\u7684\u72EC\u7ACB ArrayBuffer (\u62F7\u8D1D); ArrayBuffer \u539F\u6837\u8FD4\u56DE\u3002 */\n const toBuffer = (d: ArrayBuffer | ArrayBufferView): ArrayBuffer =>\n d instanceof ArrayBuffer ? d : (d.buffer as ArrayBuffer).slice(d.byteOffset, d.byteOffset + d.byteLength);\n const bindAbort = (signal: AbortSignal | undefined, id: string) => {\n if (!signal) return;\n const abort = () => { if (!destroyed) postToFrame('xc.audio.end', { abort: true }, id); };\n if (signal.aborted) abort(); else signal.addEventListener('abort', abort, { once: true });\n };\n const speakAudio = async (source: XiaochunAudioSource, opts: XiaochunAudioOptions = {}): Promise<void> => {\n if (destroyed) throw new Error('[project-xiaochun] instance destroyed');\n if (!iframe) activate(); // \u5148\u521B\u5EFA iframe (lazy \u6A21\u5F0F\u4E0B\u4E0E fetch \u5E76\u884C, \u7701\u4E00\u70B9\u9996\u6B21\u5EF6\u8FDF)\n const { signal, transfer, fetch: fetchWhere, ...rest } = opts;\n const payload: Record<string, unknown> = { ...rest };\n const list: Transferable[] = [];\n if (typeof source === 'string') {\n if (fetchWhere === 'frame') payload.source = source;\n else {\n const res = await fetch(source);\n if (!res.ok) throw new Error(`[project-xiaochun] audio fetch failed: HTTP ${res.status}`);\n const buf = await res.arrayBuffer();\n payload.source = buf; list.push(buf);\n payload.mimeType ??= res.headers.get('content-type') ?? undefined;\n }\n } else if (typeof Blob !== 'undefined' && source instanceof Blob) {\n payload.source = source; // Blob \u8D70\u7ED3\u6784\u5316\u514B\u9686 (\u6D4F\u89C8\u5668\u5185\u90E8\u5F15\u7528\u540C\u4E00\u4EFD\u6570\u636E, \u4E0D\u6574\u6BB5\u590D\u5236)\n payload.mimeType ??= source.type || undefined;\n } else {\n const buf = toBuffer(source as ArrayBuffer | ArrayBufferView);\n payload.source = buf;\n // \u89C6\u56FE\u603B\u662F\u62F7\u8D1D\u540E\u7684\u65B0 buffer \u2192 \u8F6C\u79FB; \u7528\u6237\u4F20\u5165\u7684 ArrayBuffer \u4EC5\u5728 transfer !== false \u65F6\u8F6C\u79FB (\u4F1A\u88AB detach)\n if (!(source instanceof ArrayBuffer) || transfer !== false) list.push(buf);\n }\n if (destroyed) throw new Error('[project-xiaochun] instance destroyed');\n const id = `c${++idSeq}`;\n const done = send('xc.audio', payload, true, list, id);\n bindAbort(signal, id); // \u5FC5\u987B\u5728 send \u4E4B\u540E (\u4FE1\u53F7\u5DF2 aborted \u65F6, abort \u6D88\u606F\u8981\u6392\u5728 xc.audio \u540E\u9762)\n return done;\n };\n const speakAudioStream = (opts: XiaochunAudioOptions & { sampleRate: number }): XiaochunAudioStream => {\n if (destroyed) throw new Error('[project-xiaochun] instance destroyed');\n const { signal, transfer, fetch: _f, mimeType: _m, ...rest } = opts;\n const id = `c${++idSeq}`;\n let started = false;\n let ended = false;\n let fmt: 'pcm16' | 'float32' | null = rest.format === 'pcm16' || rest.format === 'float32' ? rest.format : null;\n let done: Promise<void> = Promise.resolve();\n const handle: XiaochunAudioStream = {\n write(data) {\n if (ended) throw new Error('[project-xiaochun] audio stream already ended');\n if (destroyed) throw new Error('[project-xiaochun] instance destroyed');\n if (data instanceof Int16Array) fmt ??= 'pcm16';\n else if (data instanceof Float32Array) fmt ??= 'float32';\n if (!fmt) throw new Error('[project-xiaochun] speakAudioStream: set opts.format when writing raw ArrayBuffers');\n const buf = toBuffer(data);\n const body: Record<string, unknown> = { data: buf, format: fmt, sampleRate: rest.sampleRate, channels: rest.channels };\n if (!started) {\n started = true;\n body.text = rest.text; body.motion = rest.motion; body.lipsync = rest.lipsync;\n body.audible = rest.audible; body.playbackRate = rest.playbackRate; body.volume = rest.volume;\n done = send('xc.audio.chunk', body, true, [buf], id);\n bindAbort(signal, id);\n } else {\n postToFrame('xc.audio.chunk', body, id, !(data instanceof ArrayBuffer) || transfer !== false ? [buf] : []);\n }\n },\n end() {\n if (ended) return;\n ended = true;\n if (started) postToFrame('xc.audio.end', {}, id);\n },\n abort() {\n if (ended && !started) return;\n ended = true;\n if (started) postToFrame('xc.audio.end', { abort: true }, id);\n },\n get done() { return done; },\n };\n return handle;\n };\n\n // \u2500\u2500 iframe \u521B\u5EFA \u2500\u2500\n function buildUrl(): string {\n const u = new URL(srcUrl.href);\n u.searchParams.set('host', window.location.origin); // iframe \u7AEF\u7528\u5B83\u505A\u63E1\u624B\u767D\u540D\u5355\n if (transparent) u.searchParams.set('transparent', '1');\n if (uiParts.length) u.searchParams.set('ui', uiParts.join(','));\n if (options.lang) u.searchParams.set('lang', options.lang);\n if (uiAutoHide !== undefined) u.searchParams.set('uiAutoHide', String(uiAutoHide));\n if (typeof camera.fov === 'number') u.searchParams.set('cameraFov', String(camera.fov));\n if (typeof camera.distance === 'number') u.searchParams.set('cameraDistance', String(camera.distance));\n if (typeof camera.height === 'number') u.searchParams.set('cameraHeight', String(camera.height));\n if (typeof camera.intro === 'boolean') u.searchParams.set('cameraIntro', camera.intro ? '1' : '0');\n if (options.heavy) u.searchParams.set('heavy', options.heavy);\n if (options.controls === false) u.searchParams.set('controls', '0');\n if (initialScene && isXcId(initialScene)) u.searchParams.set('scene', initialScene);\n if (initialOutfit) u.searchParams.set('outfit', initialOutfit);\n return u.href;\n }\n\n function activate(): void {\n if (destroyed || iframe) return;\n cancelIdle();\n io?.disconnect(); io = null;\n const f = document.createElement('iframe');\n f.title = 'Project XiaoChun';\n f.loading = 'lazy';\n f.allow = options.crossOriginIsolated === true\n ? 'microphone; autoplay; cross-origin-isolated' // \u5BBF\u4E3B\u5DF2 COOP/COEP \u9694\u79BB\u65F6, \u989D\u5916\u59D4\u6D3E\u8DE8\u6E90\u9694\u79BB \u2192 iframe \u5185 SharedArrayBuffer / \u591A\u7EBF\u7A0B wasm\n : 'microphone; autoplay'; // \u5BBF\u4E3B\u5FC5\u987B\u59D4\u6D3E: \u9EA6\u514B\u98CE (STT) + \u81EA\u52A8\u64AD\u653E (TTS \u97F3\u9891; \u7F3A\u5B83\u5219\u70B9\u51FB\u5BBF\u4E3B\u9875\u540E iframe \u5185 AudioContext \u4ECD\u88AB\u62E6)\n f.referrerPolicy = 'strict-origin-when-cross-origin';\n if (options.sandbox !== false) {\n f.setAttribute('sandbox', options.sandbox ?? 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox');\n }\n f.setAttribute('allowtransparency', 'true');\n f.setAttribute('scrolling', 'no');\n const s = f.style;\n s.position = 'absolute'; s.inset = '0'; s.width = '100%'; s.height = '100%';\n s.border = '0'; s.background = 'transparent'; s.colorScheme = 'normal';\n s.borderRadius = `var(--xc-radius, ${radiusValue()})`; // \u5706\u89D2\u88C1\u526A iframe \u5185\u5BB9 (\u5BBF\u4E3B CSS \u53EA\u80FD\u6539\u8FD9\u5C42\u5916\u58F3, \u6539\u4E0D\u5230 iframe \u5185\u90E8)\n f.setAttribute('part', 'iframe');\n noSelect(s);\n s.opacity = '0'; s.transition = 'opacity .25s ease';\n s.pointerEvents = passthrough ? 'none' : 'auto';\n f.src = buildUrl();\n iframe = f;\n wrapper.appendChild(f);\n if (placeholderEl) placeholderEl.style.cursor = 'default';\n handshakeTimer = setTimeout(() => {\n if (!port) emit('error', { code: 'timeout', message: `no xc.ready from ${frameOrigin} within ${handshakeTimeout}ms` });\n }, handshakeTimeout);\n setupAutoPause();\n if (passthrough) enablePassthrough();\n }\n\n // \u2500\u2500 \u6765\u81EA iframe \u7684\u63E1\u624B (window message) \u2500\u2500\n const onWindowMessage = (ev: MessageEvent) => {\n if (destroyed || !iframe || ev.source !== iframe.contentWindow) return; // \u53EA\u8BA4\u6211\u4EEC\u8FD9\u4E2A iframe\n if (!trusted.has(ev.origin)) {\n emit('error', { code: 'origin_denied', message: `ignored message from untrusted origin ${ev.origin}` });\n return;\n }\n const data = ev.data;\n if (!isXcEnvelope(data) || data.type !== 'xc.ready' || port) return; // \u63E1\u624B\u53EA\u505A\u4E00\u6B21\n const ch = new MessageChannel();\n port = ch.port1;\n port.onmessage = onPortMessage;\n caps = (data.payload as XcReadyPayload | undefined)?.capabilities ?? null;\n // targetOrigin = \u5DF2\u6821\u9A8C\u7684 ev.origin, \u7EDD\u4E0D\u7528 '*'\u3002allowCustomModel \u8D70 xc.init \u7684 config (\u4E0D\u8FDB URL), \u65E7 iframe \u4F1A\u5FFD\u7565\u672A\u77E5\u5B57\u6BB5\n // \u624B\u52BF\u5F00\u5173\u968F xc.init \u4E00\u8D77\u4EA4\u7ED9 iframe (iframe \u9ED8\u8BA4\u4E0D\u8BC6\u522B\u4EFB\u4F55\u624B\u52BF); \u65E7\u7248 /embed \u6CA1\u6709 capabilities.gestures \u2192 \u4E0D\u53D1\n const gWant = wantGestures();\n sentGestures = gWant;\n const initConfig: XcConfig = {\n ...(options.allowCustomModel ? { allowCustomModel: true } : {}),\n ...(gWant.move || gWant.resize ? { gestures: gWant } : {}),\n };\n iframe.contentWindow!.postMessage(\n xcMessage('xc.init', { hostOrigin: window.location.origin, ...(Object.keys(initConfig).length ? { config: initConfig } : {}) }),\n ev.origin,\n [ch.port2],\n );\n warnGestureUnsupported();\n if (handshakeTimer) { clearTimeout(handshakeTimer); handshakeTimer = null; }\n emit('handshake', data.payload as XcReadyPayload);\n resolveHandshake();\n for (const m of queue.splice(0)) {\n if (m.requires && !caps?.[m.requires]) unsupportedByFrame(m.type, m.id);\n else port.postMessage(xcMessage(m.type, m.payload, m.id), m.transfer ?? []);\n }\n syncPause();\n };\n window.addEventListener('message', onWindowMessage);\n\n /** \u547D\u4EE4\u5E94\u7B54: \u7528\u540C\u4E00\u4E2A\u4FE1\u5C01 id resolve \u5BF9\u5E94\u7684 Promise (xc.outfit-changed / xc.scene-changed)\u3002 */\n function ack(id: string | undefined, value?: unknown) {\n const p = id ? pending.get(id) : undefined;\n if (p && id) { pending.delete(id); p.resolve(value); }\n }\n\n // \u2500\u2500 \u6765\u81EA iframe \u7684\u7AEF\u53E3\u6D88\u606F \u2500\u2500\n function onPortMessage(ev: MessageEvent) {\n const data = ev.data as XcFrameMessage;\n if (!isXcEnvelope(data)) return;\n switch (data.type) {\n case 'xc.load.progress': emit('progress', data.payload); break;\n case 'xc.loaded':\n if (!loaded) {\n loaded = true;\n // \u81EA\u52A8\u9884\u53D6: \u53EA\u5728 heavy:'eager' \u4E14 iframe \u58F0\u660E\u652F\u6301\u65F6; \u5931\u8D25 / \u90E8\u5206\u5931\u8D25\u90FD\u4E0D\u6253\u6270\u5BBF\u4E3B (\u663E\u5F0F prefetch() \u624D\u8FD4\u56DE\u7ED3\u679C)\n if (options.prefetch && options.heavy === 'eager' && caps?.prefetch) {\n const ids = Array.isArray(options.prefetch) ? options.prefetch : undefined;\n void api.prefetch(ids).catch(() => {});\n }\n if (iframe) iframe.style.opacity = '1';\n if (placeholderEl) {\n const el = placeholderEl;\n el.style.opacity = '0'; el.style.pointerEvents = 'none';\n setTimeout(() => el.remove(), 300);\n placeholderEl = null;\n }\n resolveReady();\n }\n emit('ready', data.payload);\n break;\n case 'xc.outfit-changed': {\n const pl = data.payload;\n if (!pl.noop) {\n currentOutfit = pl.id;\n emit('outfit-changed', pl);\n if (!pl.initial && pl.id) writePrefs({ outfit: pl.id }); // \u53EA\u8BB0\u7528\u6237/\u5BBF\u4E3B\u4E3B\u52A8\u6362\u7684, \u4E0D\u8BB0\u9ED8\u8BA4\u670D\u88C5\n }\n ack(data.id);\n break;\n }\n case 'xc.scene-changed': {\n const pl = data.payload;\n currentScene = pl.id;\n if (pl.transparent !== transparent) applyTransparency(pl.transparent); // iframe \u662F\u4E8B\u5B9E\u6E90: \u540C\u6B65\u5916\u58F3\u80CC\u666F / \u7A7F\u900F / pointer-events\n if (!pl.noop) {\n emit('scene-changed', pl);\n if (!pl.initial) writePrefs({ scene: pl.id });\n }\n ack(data.id);\n break;\n }\n case 'xc.lang-changed': emit('lang-changed', data.payload); break;\n case 'xc.prefetched': ack(data.id, data.payload); break;\n case 'xc.state': emit('state', data.payload); break;\n case 'xc.stt': emit('stt', data.payload); break;\n case 'xc.utterance': {\n emit('utterance', data.payload);\n const p = data.id ? pending.get(data.id) : undefined;\n if (p && data.payload.phase === 'end') { pending.delete(data.id!); p.resolve(); }\n break;\n }\n case 'xc.hit-region': {\n const { hit } = data.payload;\n emit('hit-region', data.payload);\n if (passthrough && iframe && hit !== lastHit) {\n lastHit = hit;\n iframe.style.pointerEvents = hit ? 'auto' : 'none';\n }\n break;\n }\n case 'xc.gesture-move': onGesture('move', data.payload); break;\n case 'xc.gesture-resize': onGesture('resize', data.payload); break;\n case 'xc.error': {\n const { code, message, command } = data.payload;\n emit('error', { code, message, command });\n // \u521D\u59CB ?outfit= / ?scene= \u88AB iframe \u5224\u4E3A\u975E\u6CD5: \u5DF2\u4FDD\u5B58\u7684\u504F\u597D\u662F\u574F\u7684, \u6E05\u6389\u514D\u5F97\u6BCF\u6B21\u90FD\u62A5\n if (code === 'unknown_id' && (command === 'outfit' || command === 'scene')) writePrefs({ [command]: null });\n const p = data.id ? pending.get(data.id) : undefined;\n if (p && data.id) { pending.delete(data.id); p.reject(new Error(`[${code}] ${message}`)); }\n break;\n }\n default: break;\n }\n }\n\n // \u2500\u2500 \u5BBF\u4E3B pointer \u2192 iframe (\u900F\u660E\u7A7F\u900F\u6A21\u5F0F) \u2500\u2500\n // iframe \u5904\u4E8E pointer-events:none \u65F6, \u5BBF\u4E3B\u9875\u7684 pointermove \u624D\u662F\u552F\u4E00\u7EBF\u7D22:\n // - \u6307\u9488\u4E0D\u5728 iframe \u8303\u56F4\u5185\u65F6\u53EA\u505A\u4E00\u6B21\u5EC9\u4EF7\u7684\u77E9\u5F62\u6BD4\u8F83 (\u77E9\u5F62\u7F13\u5B58, \u4E0D\u5728\u6BCF\u5E27 getBoundingClientRect \u4EE5\u514D\u5F3A\u5236\u56DE\u6D41);\n // - \u6307\u9488\u5728\u8303\u56F4\u5185\u65F6\u6BCF\u5E27\u6700\u591A\u53D1\u4E00\u6B21 xc.pointer, \u4E14\u79FB\u52A8\u4E0D\u8DB3 2px \u6216\u8DDD\u4E0A\u6B21\u4E0D\u8DB3 32ms \u5219\u8DF3\u8FC7, \u907F\u514D iframe \u91CC\u6BCF\u5E27\u4E00\u6B21\u5C04\u7EBF\u68C0\u6D4B\u3002\n const POINTER_MIN_INTERVAL_MS = 32;\n const POINTER_MIN_MOVE_PX = 2;\n let pointerRaf = 0;\n let lastPtr: { x: number; y: number } | null = null;\n let lastPosted: { x: number; y: number; t: number } | null = null;\n let rectCache: DOMRect | null = null;\n let rectRo: ResizeObserver | null = null;\n let rectAt = 0;\n const invalidateRect = () => { rectCache = null; };\n const getRect = (): DOMRect | null => {\n if (!iframe) return null;\n const now = performance.now();\n // \u515C\u5E95 TTL: \u5185\u8054\u5E03\u5C40\u88AB\u9875\u9762\u5176\u5B83\u5185\u5BB9\u63A8\u52A8 (\u4E0D\u89E6\u53D1 resize/scroll) \u65F6, \u6700\u591A 400ms \u540E\u81EA\u6108\n if (!rectCache || now - rectAt > 400) { rectCache = iframe.getBoundingClientRect(); rectAt = now; }\n return rectCache;\n };\n const onHostPointer = (e: PointerEvent) => {\n if (!iframe || !port || iframe.style.pointerEvents === 'auto') return; // iframe \u81EA\u5DF1\u62FF\u5230\u4E8B\u4EF6\u65F6\u7531\u5B83\u4E0A\u62A5\n lastPtr = { x: e.clientX, y: e.clientY };\n if (pointerRaf) return;\n pointerRaf = requestAnimationFrame(() => {\n pointerRaf = 0;\n if (!iframe || !port || !lastPtr) return;\n const r = getRect();\n if (!r) return;\n const x = lastPtr.x - r.left, y = lastPtr.y - r.top;\n if (x < 0 || y < 0 || x > r.width || y > r.height) {\n lastPosted = null;\n if (lastHit) { lastHit = false; iframe.style.pointerEvents = 'none'; }\n return;\n }\n const now = performance.now();\n if (lastPosted && now - lastPosted.t < POINTER_MIN_INTERVAL_MS) return;\n if (lastPosted && Math.abs(x - lastPosted.x) < POINTER_MIN_MOVE_PX && Math.abs(y - lastPosted.y) < POINTER_MIN_MOVE_PX) return;\n lastPosted = { x, y, t: now };\n port.postMessage(xcMessage('xc.pointer', { x, y }));\n });\n };\n // \u7A7F\u900F\u76D1\u542C\u53EF\u542F\u505C (\u8FD0\u884C\u4E2D\u5207 scene \u65F6\u9700\u8981): enable / disable \u90FD\u5E42\u7B49\u3002\n let passthroughActive = false;\n function enablePassthrough() {\n if (passthroughActive || !iframe) return;\n passthroughActive = true;\n window.addEventListener('pointermove', onHostPointer, { passive: true });\n // \u89E6\u5C4F\u6CA1\u6709 hover: \u70B9\u4E00\u4E0B (pointerdown) \u4E5F\u63A2\u6D4B\u4E00\u6B21\u547D\u4E2D, \u547D\u4E2D\u540E iframe \u8F6C auto, \u4E0B\u4E00\u6B21\u70B9\u51FB\u5373\u53EF\u843D\u5230\u89D2\u8272 / \u5185\u7F6E\u6309\u94AE\u4E0A\n window.addEventListener('pointerdown', onHostPointer, { passive: true });\n window.addEventListener('scroll', invalidateRect, { passive: true, capture: true });\n window.addEventListener('resize', invalidateRect, { passive: true });\n if (typeof ResizeObserver !== 'undefined') {\n rectRo = new ResizeObserver(invalidateRect);\n rectRo.observe(iframe);\n }\n }\n function disablePassthrough() {\n if (!passthroughActive) return;\n passthroughActive = false;\n window.removeEventListener('pointermove', onHostPointer);\n window.removeEventListener('pointerdown', onHostPointer);\n window.removeEventListener('scroll', invalidateRect, true);\n window.removeEventListener('resize', invalidateRect);\n rectRo?.disconnect(); rectRo = null;\n if (pointerRaf) { cancelAnimationFrame(pointerRaf); pointerRaf = 0; }\n lastPtr = null;\n }\n /**\n * \u8FD0\u884C\u4E2D\u5207\u6362\u900F\u660E \u2194 \u975E\u900F\u660E (xc.scene-changed \u9A71\u52A8): \u540C\u6B65\u5916\u58F3\u80CC\u666F\u3001\u5F00\u5173\u7A7F\u900F\u76D1\u542C\u3001\u91CD\u7F6E\u547D\u4E2D\u72B6\u6001\u3002\n * - \u5207\u5230\u975E\u900F\u660E: \u5F3A\u5236 iframe pointer-events:auto (\u6574\u5757\u753B\u5E03\u90FD\u662F\u89D2\u8272\u80CC\u666F, \u4E0D\u8BE5\u7A7F\u900F), \u5173\u6389\u7A7F\u900F\u76D1\u542C;\n * - \u5207\u5230\u900F\u660E: passthrough \u9009\u9879\u663E\u5F0F\u7ED9\u4E86\u5C31\u6309\u5B83, \u5426\u5219\u6253\u5F00; iframe \u5148 none, \u7B49 xc.pointer / xc.hit-region \u518D\u5207 auto;\n * - \u4E24\u79CD\u60C5\u51B5\u90FD\u6E05\u6389 lastHit / lastPosted / \u77E9\u5F62\u7F13\u5B58, \u5426\u5219 iframe \u4FA7\u5DF2\u91CD\u7F6E\u7684\u53BB\u91CD\u72B6\u6001\u4F1A\u548C\u8FD9\u91CC\u5BF9\u4E0D\u4E0A, \u5BFC\u81F4\u6307\u9488\u4E0D\u52A8\u65F6\u7B2C\u4E00\u6B21\u547D\u4E2D\u88AB\u541E\u3002\n */\n function applyTransparency(next: boolean) {\n transparent = next;\n applyWrapperBg();\n applyRadius();\n passthrough = next ? (options.passthrough ?? true) : false;\n lastHit = false; lastPosted = null; rectCache = null;\n if (passthrough) enablePassthrough(); else disablePassthrough();\n if (iframe) iframe.style.pointerEvents = passthrough ? 'none' : 'auto';\n }\n\n // \u2500\u2500 \u89C6\u53E3\u5916\u81EA\u52A8\u6682\u505C \u2500\u2500\n let pauseIo: IntersectionObserver | null = null;\n function setupAutoPause() {\n if (options.autoPause === false || typeof IntersectionObserver === 'undefined') return;\n pauseIo = new IntersectionObserver((entries) => {\n const visible = entries[entries.length - 1]?.isIntersecting ?? true;\n autoPaused = !visible;\n syncPause();\n });\n pauseIo.observe(wrapper);\n }\n let sentPaused = false;\n function syncPause() {\n if (!port) return;\n const want = userPaused || autoPaused;\n if (want === sentPaused) return;\n sentPaused = want;\n port.postMessage(xcMessage(want ? 'xc.pause' : 'xc.resume'));\n }\n\n // \u2500\u2500 lazy \u8C03\u5EA6 \u2500\u2500\n function cancelIdle() {\n if (idleHandle === null) return;\n const w = window as Window & { cancelIdleCallback?: (h: number) => void };\n if (w.cancelIdleCallback) w.cancelIdleCallback(idleHandle); else clearTimeout(idleHandle);\n idleHandle = null;\n }\n function scheduleLazy() {\n if (lazy === false) { activate(); return; }\n if (lazy === 'click') return;\n const go = () => {\n const w = window as Window & { requestIdleCallback?: (cb: () => void, o?: { timeout: number }) => number };\n // \u7A7A\u95F2\u56DE\u8C03 timeout 3s: \u4E3B\u7EBF\u7A0B\u4E00\u76F4\u5FD9\u4E5F\u6700\u8FDF 3s \u540E\u521B\u5EFA, \u8C03\u5927\u66F4\u4E0D\u5F71\u54CD\u9996\u5C4F, \u8C03\u5C0F\u66F4\u5FEB\u51FA\u73B0\n idleHandle = w.requestIdleCallback ? w.requestIdleCallback(() => { idleHandle = null; activate(); }, { timeout: 3000 }) : (setTimeout(activate, 1200) as unknown as number);\n };\n if (typeof IntersectionObserver === 'undefined') { go(); return; }\n io = new IntersectionObserver((entries) => {\n if (entries.some((e) => e.isIntersecting)) { io?.disconnect(); io = null; go(); }\n }, { rootMargin: `${options.lazyMargin ?? 200}px` });\n io.observe(wrapper);\n }\n\n // \u2500\u2500 \u624B\u52BF: \u62D6\u52A8 / \u7F29\u653E (iframe \u91CC\u8BC6\u522B, \u5BBF\u4E3B\u8FD9\u91CC\u6267\u884C + \u9650\u5E45) \u2500\u2500\n // \u534F\u8BAE: xc.gesture-move / xc.gesture-resize (\u89C1 protocol.ts \u7684 XcGestureBase)\u3002\u5BBF\u4E3B\u4E0D\u4FE1\u4EFB iframe \u53D1\u6765\u7684\u4EFB\u4F55\u6570:\n // \u6821\u9A8C\u8F7D\u8377 (parseGesturePayload) \u2192 \u5E8F\u53F7\u95F8\u95E8 (GestureGate) \u2192 \u4EE5\"\u624B\u52BF\u5F00\u59CB\u65F6\u7684\u76D2\u5B50 + \u7D2F\u8BA1\u4F4D\u79FB\"\u5939\u5728\u89C6\u53E3 / \u6700\u5C0F\u6700\u5927\u5C3A\u5BF8\u5185 (gesture-box.ts)\u3002\n // \u5BBF\u4E3B\u6CA1\u5F00 draggable / resizable \u65F6, \u5BF9\u5E94\u6D88\u606F\u4E00\u5F8B\u5FFD\u7565; \u540C\u4E00\u65F6\u523B\u53EA\u5141\u8BB8\u4E00\u4E2A\u624B\u52BF\u3002\n let draggableOn = options.draggable === true;\n let resizeLimits: ResizeLimits | null = normalizeResizable(options.resizable);\n const gates = { move: new GestureGate(), resize: new GestureGate() };\n let activeGesture: { kind: 'move' | 'resize'; start: Box; inlineBase: { left: number; top: number } | null; corner?: 'NW' | 'NE' | 'SW' | 'SE' } | null = null;\n /** \u5185\u8054\u6A21\u5F0F\u9760 CSS translate \u4F4D\u79FB (\u4E0D\u6539\u5E03\u5C40\u6D41); \u8BB0\u4E0B\u5F53\u524D\u4F4D\u79FB, \u624D\u80FD\u8FD8\u539F\u51FA\"\u6D41\u5185\u539F\u70B9\"\u3002 */\n let inlineShift = { x: 0, y: 0 };\n let userMoved = false;\n let sentGestures: { move: boolean; resize: boolean } | null = null;\n let warnedGesture = false;\n const viewport = (): Viewport => ({\n width: document.documentElement.clientWidth || window.innerWidth,\n height: document.documentElement.clientHeight || window.innerHeight,\n });\n const readBox = (): Box => { const r = wrapper.getBoundingClientRect(); return { left: r.left, top: r.top, width: r.width, height: r.height }; };\n const wantGestures = () => ({ move: draggableOn && caps?.gestures?.move === true, resize: resizeLimits !== null && caps?.gestures?.resize === true });\n\n function applyBox(b: Box, inlineBase: { left: number; top: number } | null, withSize: boolean): void {\n if (withSize) { ws.width = `${b.width}px`; ws.height = `${b.height}px`; ws.maxWidth = 'none'; } // maxWidth:100% \u4F1A\u628A\u7528\u6237\u62C9\u5927\u7684\u5BBD\u5EA6\u538B\u56DE\u5BB9\u5668\u5BBD\n if (position === 'inline') {\n const base = inlineBase ?? { left: b.left - inlineShift.x, top: b.top - inlineShift.y };\n inlineShift = { x: b.left - base.left, y: b.top - base.top };\n ws.setProperty('translate', `${inlineShift.x}px ${inlineShift.y}px`);\n } else {\n ws.left = `${b.left}px`; ws.top = `${b.top}px`; ws.right = 'auto'; ws.bottom = 'auto';\n }\n userMoved = true;\n invalidateRect();\n }\n\n // \u62D6\u52A8\u671F\u95F4\u4E0D\u51FA\u73B0\u9009\u4E2D\u84DD\u6846: \u5BBF\u4E3B\u9875\u4E34\u65F6 user-select:none \u5E76\u6E05\u6389\u5DF2\u6709\u9009\u533A (iframe \u5185\u90E8\u53E6\u6709\u5904\u7406)\n let selectionLock: { us: string; wus: string } | null = null;\n function lockSelection(on: boolean): void {\n const de = document.documentElement;\n if (on && !selectionLock) {\n selectionLock = { us: de.style.getPropertyValue('user-select'), wus: de.style.getPropertyValue('-webkit-user-select') };\n de.style.setProperty('user-select', 'none'); de.style.setProperty('-webkit-user-select', 'none');\n try { window.getSelection()?.removeAllRanges(); } catch { /* ignore */ }\n } else if (!on && selectionLock) {\n de.style.setProperty('user-select', selectionLock.us); de.style.setProperty('-webkit-user-select', selectionLock.wus);\n selectionLock = null;\n }\n }\n function endActiveGesture(kind?: 'move' | 'resize'): void {\n if (!activeGesture || (kind && activeGesture.kind !== kind)) return;\n gates[activeGesture.kind].close();\n const b = readBox();\n emit(activeGesture.kind, { phase: 'end', ...b });\n activeGesture = null;\n lockSelection(false);\n saveBox();\n }\n\n function onGesture(kind: 'move' | 'resize', raw: unknown): void {\n if (destroyed || !caps?.gestures) return;\n if (kind === 'move' ? !draggableOn : resizeLimits === null) return; // \u5BBF\u4E3B\u6CA1\u5F00: \u5FFD\u7565\n const p = parseGesturePayload(kind, raw);\n if (!p) return;\n if (p.phase === 'start' ? activeGesture !== null : activeGesture?.kind !== kind) return; // \u540C\u4E00\u65F6\u523B\u53EA\u5141\u8BB8\u4E00\u4E2A\u624B\u52BF\n if (!gates[kind].accept(p)) return;\n if (p.phase === 'start') {\n const start = readBox();\n activeGesture = {\n kind, start, corner: p.corner,\n inlineBase: position === 'inline' ? { left: start.left - inlineShift.x, top: start.top - inlineShift.y } : null,\n };\n lockSelection(true);\n emit(kind, { phase: 'start', ...start });\n return;\n }\n const a = activeGesture!;\n const vp = viewport();\n const next = kind === 'move'\n ? moveBox(a.start, p.totalDx, p.totalDy, vp)\n : resizeBox(a.start, a.corner!, p.totalDx, p.totalDy, resizeLimits!, vp);\n applyBox(next, a.inlineBase, kind === 'resize');\n emit(kind, { phase: p.phase, ...next });\n if (p.phase === 'end') { activeGesture = null; lockSelection(false); saveBox(); }\n }\n\n /** \u628A draggable / resizable \u7684\u5F53\u524D\u503C\u540C\u6B65\u7ED9 iframe (\u53EA\u5728\u53D8\u5316\u65F6\u53D1 xc.setConfig{gestures}); \u63E1\u624B\u65F6\u5DF2\u968F xc.init \u53D1\u8FC7\u4E00\u6B21\u3002 */\n function syncGestures(): void {\n if (destroyed) return;\n const want = wantGestures();\n if (!want.move) endActiveGesture('move');\n if (!want.resize) endActiveGesture('resize');\n if (!port || !caps) return;\n warnGestureUnsupported();\n if (sentGestures && sentGestures.move === want.move && sentGestures.resize === want.resize) return;\n sentGestures = want;\n if (caps.gestures) port.postMessage(xcMessage('xc.setConfig', { gestures: want }));\n }\n /** \u65E7\u7248 /embed (\u63E1\u624B\u91CC\u6CA1\u6709 capabilities.gestures) \u5374\u5F00\u4E86\u624B\u52BF: \u544A\u77E5\u4E00\u6B21\u3002 */\n function warnGestureUnsupported(): void {\n if (warnedGesture || !caps || caps.gestures) return;\n const needMove = draggableOn;\n if (!needMove && resizeLimits === null) return;\n warnedGesture = true;\n const what = [needMove ? 'draggable' : '', resizeLimits !== null ? 'resizable' : ''].filter(Boolean).join(' / ');\n emit('error', { code: 'unsupported', message: `${what} needs gesture support (capabilities.gestures), which this /embed deployment does not advertise (older version)`, command: 'gestures' });\n }\n\n // \u7A97\u53E3\u53D8\u5C0F\u540E\u628A\u7528\u6237\u62D6\u8FC7 / \u7F29\u8FC7\u7684\u5934\u50CF\u5939\u56DE\u89C6\u53E3 (\u6CA1\u52A8\u8FC7\u5C31\u4E0D\u7BA1, \u4EA4\u7ED9\u9875\u9762\u81EA\u5DF1\u7684\u5E03\u5C40)\n const onWindowResize = () => {\n if (!userMoved || activeGesture || destroyed) return;\n const cur = readBox();\n const vp = viewport();\n // \u5185\u8054\u76D2\u5B50\u53EF\u4EE5\u5728\u6EDA\u52A8\u7684\u9875\u9762\u4E0B\u65B9: \u7EB5\u5411\u4E0D\u6309\u89C6\u53E3\u5939 (\u5426\u5219\u4F1A\u628A\u89C6\u53E3\u5916\u7684\u76D2\u5B50\u62FD\u8FDB\u89C6\u53E3), \u53EA\u4FDD\u8BC1\u4E0D\u8D8A\u8FC7\u6587\u6863\u9876\u90E8; \u6A2A\u5411 / \u5C3A\u5BF8\u7167\u5E38\u5939\u8FDB\u89C6\u53E3\n const fit = position === 'inline'\n ? { ...fitBox({ ...cur, top: 0 }, { width: vp.width, height: Number.MAX_SAFE_INTEGER }), top: Math.max(cur.top, -window.scrollY) }\n : fitBox(cur, vp);\n if (fit.left === cur.left && fit.top === cur.top && fit.width === cur.width && fit.height === cur.height) return;\n applyBox(fit, position === 'inline' ? { left: cur.left - inlineShift.x, top: cur.top - inlineShift.y } : null, fit.width !== cur.width || fit.height !== cur.height);\n };\n window.addEventListener('resize', onWindowResize, { passive: true });\n\n // \u2500\u2500 \u4F4D\u7F6E / \u5927\u5C0F\u8BB0\u5FC6 (persistBox) \u2500\u2500\n // \u5B58\u7684\u662F { v, mode, x, y, width, height }: \u60AC\u6D6E\u6A21\u5F0F x/y = \u89C6\u53E3 left/top; \u5185\u8054\u6A21\u5F0F x/y = \u76F8\u5BF9\"\u6D41\u5185\u539F\u70B9\"\u7684\u4F4D\u79FB (translate), \u4E0E\u6EDA\u52A8\u65E0\u5173\u3002\n const boxKey = options.persistBox === true ? 'xiaochun:box' : typeof options.persistBox === 'string' && options.persistBox ? `xiaochun:box:${options.persistBox}` : null;\n const saveBox = (): void => {\n if (!boxKey) return;\n try {\n const b = readBox();\n const at = position === 'inline' ? inlineShift : { x: b.left, y: b.top };\n window.localStorage.setItem(boxKey, JSON.stringify({ v: 1, mode: position, x: Math.round(at.x), y: Math.round(at.y), width: Math.round(b.width), height: Math.round(b.height) }));\n } catch { /* \u9690\u79C1\u6A21\u5F0F / \u914D\u989D / \u88AB\u7981\u7528: \u53EA\u662F\u4E0D\u8BB0\u5FC6 */ }\n };\n const loadBox = (): { x: number; y: number; width: number; height: number } | null => {\n if (!boxKey) return null;\n try {\n const raw = window.localStorage.getItem(boxKey);\n if (raw === null) return null;\n let o: Record<string, unknown> | null = null;\n try { o = JSON.parse(raw) as Record<string, unknown> | null; } catch { /* \u574F JSON: \u4E0B\u9762\u4E00\u5E76\u6E05\u6389 */ }\n const ok = (v: unknown, lo: number, hi: number): v is number => typeof v === 'number' && Number.isFinite(v) && v >= lo && v <= hi;\n if (o && o.v === 1 && o.mode === position && ok(o.x, -1e5, 1e5) && ok(o.y, -1e5, 1e5) && ok(o.width, 1, 1e5) && ok(o.height, 1, 1e5)) {\n return { x: o.x, y: o.y, width: o.width, height: o.height };\n }\n window.localStorage.removeItem(boxKey); // \u574F\u6570\u636E / \u522B\u7684\u7248\u672C / \u522B\u7684\u5B9A\u4F4D\u6A21\u5F0F: \u6E05\u6389\n } catch { /* ignore */ }\n return null;\n };\n /** \u521B\u5EFA\u65F6\u6062\u590D: \u9700\u8981\u624B\u52BF\u5F00\u7740; \u663E\u5F0F width / height \u9009\u9879\u4F18\u5148\u4E8E\u5DF2\u4FDD\u5B58\u7684\u5927\u5C0F; \u5168\u90E8\u94B3\u5230\u5F53\u524D\u89C6\u53E3\u3002 */\n function restoreBox(): void {\n if (!boxKey || (!draggableOn && resizeLimits === null)) return;\n const st = loadBox();\n if (!st) return;\n const vp = viewport();\n const lim = resizeLimits ?? DEFAULT_RESIZE_LIMITS;\n const cur = readBox();\n const useW = resizeLimits !== null && options.width === undefined;\n const useH = resizeLimits !== null && options.height === undefined;\n const clampN = (v: number, lo: number, hi: number) => Math.min(Math.max(v, lo), Math.max(lo, hi));\n const width = useW ? clampN(st.width, lim.minWidth, Math.min(lim.maxWidth, vp.width)) : cur.width;\n const height = useH ? clampN(st.height, lim.minHeight, Math.min(lim.maxHeight, vp.height)) : cur.height;\n if (position === 'inline') {\n const base = { left: cur.left - inlineShift.x, top: cur.top - inlineShift.y };\n const left = clampN(base.left + st.x, 0, vp.width - width);\n const top = Math.max(base.top + st.y, -window.scrollY); // \u7EB5\u5411: \u4E0D\u8D8A\u8FC7\u6587\u6863\u9876\u90E8 (\u5185\u8054\u76D2\u5B50\u53EF\u4EE5\u5728\u6EDA\u52A8\u7684\u9875\u9762\u4E0B\u65B9, \u4E0D\u6309\u89C6\u53E3\u5939)\n applyBox({ left, top, width, height }, base, useW || useH);\n } else {\n applyBox(fitBox({ left: st.x, top: st.y, width, height }, vp), null, useW || useH);\n }\n }\n restoreBox();\n\n scheduleLazy();\n\n const api: XiaochunInstance = {\n element: wrapper,\n get iframe() { return iframe; },\n ready,\n activate,\n say: (text, opts) => send('xc.say', { text, mode: opts?.mode }, true),\n speakAudio,\n speakAudioStream,\n motion: (m) => send('xc.motion', typeof m === 'string' ? (m.startsWith('http') || m.startsWith('/') ? { url: m } : { name: m }) : m),\n expression: (name) => send('xc.expression', { name }),\n lookAt: (x, y) => send('xc.lookAt', { x, y }),\n setOutfit: (id) => {\n if (!isXcId(id)) return Promise.reject(new Error('[bad_request] invalid outfit id'));\n return send('xc.setOutfit', { id }, true, undefined, undefined, 'outfits');\n },\n setScene: (id) => {\n if (!isXcId(id)) return Promise.reject(new Error('[bad_request] invalid scene id'));\n return send('xc.setScene', { id }, true, undefined, undefined, 'scenes');\n },\n prefetch: (ids) => {\n if (ids !== undefined && (!Array.isArray(ids) || ids.some((x) => !isXcId(x)))) {\n return Promise.reject(new Error('[bad_request] prefetch ids must be an array of outfit ids'));\n }\n return send('xc.prefetch', ids ? { ids } : {}, true, undefined, undefined, 'prefetch');\n },\n getOutfits: async () => { if (!iframe) activate(); await handshaked; return caps?.outfits ? caps.outfits.slice() : []; },\n getScenes: async () => { if (!iframe) activate(); await handshaked; return caps?.scenes ? caps.scenes.slice() : []; },\n get outfit() { return currentOutfit; },\n get scene() { return currentScene; },\n setModel: (m) => {\n const msg = typeof m === 'string' ? (/^(https?:)?\\/|\\.vrm/i.test(m) ? { url: m } : { outfit: m }) : m;\n if (msg.url !== undefined && msg.outfit === undefined && !options.allowCustomModel) {\n const message = 'custom model URLs are disabled; pass allowCustomModel: true to createXiaochun() (only for URLs you trust) or use setOutfit() with a built-in id';\n emit('error', { code: 'unsupported', message, command: 'xc.setModel' });\n return Promise.reject(new Error(`[unsupported] ${message}`));\n }\n return send('xc.setModel', msg);\n },\n setConfig: (cfg) => send('xc.setConfig', cfg),\n setSize(width, height) {\n if (destroyed) return;\n ws.width = css(width, defaultWidthCss(position !== 'inline')); ws.height = css(height, defaultHeightCss(position !== 'inline'));\n if (ws.maxWidth === 'none') ws.maxWidth = '100%'; // \u7528\u6237\u62C9\u5927\u8FC7 (applyBox \u4F1A\u6E05\u6389 max-width): \u5BBF\u4E3B\u91CD\u65B0\u7ED9\u5C3A\u5BF8\u540E\u6062\u590D\u4E0D\u6EA2\u51FA\u5BB9\u5668\u7684\u7EA6\u675F\n invalidateRect();\n },\n getBox: () => readBox(),\n clearPersistedBox(opts) {\n if (boxKey) { try { window.localStorage.removeItem(boxKey); } catch { /* ignore */ } }\n if (destroyed || opts?.reset !== true) return;\n endActiveGesture();\n ws.width = css(options.width, defaultWidthCss(position !== 'inline')); ws.height = css(options.height, defaultHeightCss(position !== 'inline')); ws.maxWidth = '100%';\n if (position === 'inline') { inlineShift = { x: 0, y: 0 }; ws.removeProperty('translate'); }\n else {\n ws.left = ''; ws.top = ''; ws.right = ''; ws.bottom = '';\n if (position === 'bottom-right') { ws.right = 'var(--xc-offset-x, 16px)'; ws.bottom = 'var(--xc-offset-y, 16px)'; }\n if (position === 'bottom-left') { ws.left = 'var(--xc-offset-x, 16px)'; ws.bottom = 'var(--xc-offset-y, 16px)'; }\n }\n userMoved = false;\n invalidateRect();\n },\n setBorderRadius(radius) {\n radiusOpt = radius;\n applyRadius();\n },\n setDraggable(on) {\n draggableOn = on === true;\n syncGestures();\n },\n setResizable(on) {\n resizeLimits = normalizeResizable(on);\n syncGestures();\n },\n startListening: () => send('xc.mic', { enabled: true }),\n stopListening: () => send('xc.mic', { enabled: false }),\n mic: (enabled) => send('xc.mic', { enabled }),\n pause() { userPaused = true; syncPause(); },\n resume() { userPaused = false; syncPause(); },\n setPlaybackRate(rate) {\n if (destroyed || typeof rate !== 'number' || !Number.isFinite(rate)) return;\n postToFrame('xc.transport', { playbackRate: rate });\n },\n setVolume(volume) {\n if (destroyed || typeof volume !== 'number' || !Number.isFinite(volume)) return;\n postToFrame('xc.transport', { volume });\n },\n destroy() {\n if (destroyed) return;\n try { port?.postMessage(xcMessage('xc.destroy')); } catch { /* ignore */ }\n destroyed = true;\n cancelIdle();\n io?.disconnect(); pauseIo?.disconnect();\n if (handshakeTimer) clearTimeout(handshakeTimer);\n if (pointerRaf) cancelAnimationFrame(pointerRaf);\n window.removeEventListener('message', onWindowMessage);\n window.removeEventListener('resize', onWindowResize);\n lockSelection(false);\n disablePassthrough();\n resolveHandshake(); // \u8FD8\u5728\u7B49 getOutfits() / getScenes() \u7684\u8C03\u7528\u65B9\u62FF\u5230 [] \u800C\u4E0D\u662F\u6C38\u8FDC\u6302\u8D77\n try { port?.close(); } catch { /* ignore */ }\n port = null;\n for (const p of pending.values()) p.reject(new Error('[project-xiaochun] instance destroyed'));\n pending.clear();\n queue.length = 0;\n if (!loaded) rejectReady(new Error('[project-xiaochun] destroyed before ready'));\n wrapper.remove(); // \u540C\u65F6\u79FB\u9664 iframe \u2192 \u6D4F\u89C8\u5668\u91CA\u653E WebGL \u4E0A\u4E0B\u6587\n iframe = null;\n emit('destroy', undefined);\n listeners.clear();\n },\n on(event, cb) {\n let set = listeners.get(event);\n if (!set) { set = new Set(); listeners.set(event, set); }\n set.add(cb as (p: any) => void);\n return () => { set!.delete(cb as (p: any) => void); };\n },\n };\n return api;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }