virtual-human-cf 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -113,12 +113,24 @@ const onEnded = () => {
113
113
  | 事件名 | 说明 | 回调参数 |
114
114
  |--------|------|----------|
115
115
  | connected | WebSocket 连接成功 | - |
116
- | highlight | 高亮大屏某区域 | 载荷数据 |
117
- | showDialog | 显示弹窗 | 载荷数据 |
116
+ | eventNotifaction | 自定义事件接收器 | 载荷数据 |
118
117
  | end | 数字人对话结束 | 载荷数据 |
119
118
  | pause | 暂停播放 | 载荷数据 |
120
119
  | error | 错误事件 | 错误信息 |
121
- | play_complete | 播放完成 | - |
120
+ | playComplete | 播放完成,数字人对话结束,关闭数字人 | - |
121
+
122
+ ```json
123
+ eventNotifaction => 载荷数据 :{
124
+
125
+ "type": "send_event",
126
+ // 自定义事件名称
127
+ "event": "heightlight",
128
+ // 自定义事件参数
129
+ "params": {
130
+ "domid": "wrap-1"
131
+ }
132
+ }
133
+ ```
122
134
 
123
135
  #### 示例
124
136
 
@@ -128,8 +140,8 @@ const onEnded = () => {
128
140
  :screen-client-id="clientId"
129
141
  :ws-url="websocketUrl"
130
142
  @connected="onConnected"
131
- @highlight="onHighlight"
132
- @showDialog="onShowDialog"
143
+ @eventNotifaction="onEventNotifaction"
144
+ @playComplete="onPlayComplete"
133
145
  @end="onEnd"
134
146
  @error="onError"
135
147
  >
@@ -147,12 +159,12 @@ const onConnected = () => {
147
159
  console.log('WebSocket 连接成功');
148
160
  };
149
161
 
150
- const onHighlight = (payload) => {
151
- console.log('高亮事件:', payload);
162
+ const onEventNotifaction = (payload) => {
163
+ console.log('自定义事件接收:', payload);
152
164
  };
153
165
 
154
- const onShowDialog = (payload) => {
155
- console.log('显示弹窗:', payload);
166
+ const onPlayComplete = () => {
167
+ console.log('播放完成');
156
168
  };
157
169
 
158
170
  const onEnd = (payload) => {
@@ -174,23 +186,25 @@ const onError = (error) => {
174
186
  <div class="container">
175
187
  <VirtualHumanPersona
176
188
  :video-src="videoUrl"
189
+ :visible="personaVisible"
177
190
  :is-playing="isPlaying"
178
191
  :muted="true"
179
192
  :is-dark="isDarkMode"
180
- :screen-client-id="clientId"
193
+ :screen-client-id="activeScreenClientId"
181
194
  :ws-url="websocketUrl"
182
- @update:isPlaying="(val) => isPlaying = val"
195
+ @update:isPlaying="onPersonaPlayingUpdate"
196
+ @update:visible="onPersonaVisibleUpdate"
183
197
  @ended="onVideoEnded"
184
198
  />
185
199
 
186
200
  <VirtualHumanEventAdapter
187
- :screen-client-id="clientId"
201
+ :screen-client-id="activeScreenClientId"
188
202
  :ws-url="websocketUrl"
189
203
  @connected="onConnected"
190
- @highlight="onHighlight"
191
- @showDialog="onShowDialog"
204
+ @eventNotifaction="onEventNotifaction"
192
205
  @end="onEnd"
193
206
  @error="onError"
207
+ @playComplete="onPlayComplete"
194
208
  />
195
209
  </div>
196
210
  </template>
@@ -202,7 +216,8 @@ import { VirtualHumanPersona, VirtualHumanEventAdapter } from 'virtual-human-cf'
202
216
  const videoUrl = ref('/path/to/digital-human-video.mp4');
203
217
  const isPlaying = ref(false);
204
218
  const isDarkMode = ref(false);
205
- const clientId = ref('screen-123');
219
+ const personaVisible = ref(true);
220
+ const activeScreenClientId = ref('screen-123');
206
221
  const websocketUrl = ref('ws://localhost:8080/ws');
207
222
 
208
223
  const onConnected = () => {
@@ -213,19 +228,22 @@ const onVideoEnded = () => {
213
228
  console.log('视频播放结束');
214
229
  };
215
230
 
216
- const onHighlight = (payload) => {
217
- console.log('收到高亮指令:', payload);
218
- };
219
-
220
- const onShowDialog = (payload) => {
221
- console.log('显示对话框:', payload);
222
- };
223
231
 
224
232
  const onEnd = (payload) => {
225
233
  console.log('数字人交互结束:', payload);
226
234
  isPlaying.value = false;
227
235
  };
228
236
 
237
+ const onEventNotifaction = (payload) => {
238
+ console.log('自定义事件接收:', payload);
239
+ };
240
+
241
+ const onPlayComplete = () => {
242
+ console.log('播放完成');
243
+ // 关闭数字人
244
+ personaVisible.value = false;
245
+ }
246
+
229
247
  const onError = (error) => {
230
248
  console.error('发生错误:', error);
231
249
  };
@@ -13,11 +13,10 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
13
13
  }>, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
14
14
  error: (...args: any[]) => void;
15
15
  pause: (...args: any[]) => void;
16
- highlight: (...args: any[]) => void;
17
- showDialog: (...args: any[]) => void;
16
+ eventNotifaction: (...args: any[]) => void;
18
17
  end: (...args: any[]) => void;
19
18
  connected: (...args: any[]) => void;
20
- play_complete: (...args: any[]) => void;
19
+ playComplete: (...args: any[]) => void;
21
20
  }, string, import('vue').PublicProps, Readonly<import('vue').ExtractPropTypes<{
22
21
  screenClientId: {
23
22
  type: StringConstructor;
@@ -30,11 +29,10 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
30
29
  }>> & Readonly<{
31
30
  onError?: ((...args: any[]) => any) | undefined;
32
31
  onPause?: ((...args: any[]) => any) | undefined;
33
- onHighlight?: ((...args: any[]) => any) | undefined;
34
- onShowDialog?: ((...args: any[]) => any) | undefined;
32
+ onEventNotifaction?: ((...args: any[]) => any) | undefined;
35
33
  onEnd?: ((...args: any[]) => any) | undefined;
36
34
  onConnected?: ((...args: any[]) => any) | undefined;
37
- onPlay_complete?: ((...args: any[]) => any) | undefined;
35
+ onPlayComplete?: ((...args: any[]) => any) | undefined;
38
36
  }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
39
37
  declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
40
38
  export default _default;
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- .fade-enter-active[data-v-5d741303],.fade-leave-active[data-v-5d741303]{transition:opacity .5s ease,transform .5s ease}.fade-enter-from[data-v-5d741303],.fade-leave-to[data-v-5d741303]{opacity:0;transform:translateY(10px)}.virtual-human-container[data-v-5d741303]{position:fixed;z-index:2147483647;overflow:visible}.video-wrapper[data-v-5d741303]{position:relative;width:100%;height:100%;border-radius:1rem;overflow:hidden;box-shadow:0 10px 25px #0000001a;background:#fff;border:2px solid transparent;transition:background-color .3s ease,box-shadow .3s ease,border-color .3s ease}.video-wrapper[data-v-5d741303]:hover{border-color:#409eff}.virtual-human-container.is-dark .video-wrapper[data-v-5d741303]{background:#1f2937;box-shadow:0 10px 25px #00000080}.persona-video[data-v-5d741303]{width:100%;height:100%;object-fit:cover;transform-origin:center;transition:transform .1s ease-out}.resize-handle[data-v-5d741303]{position:absolute;width:20px;height:20px;background:#00000080;border:2px solid rgba(255,255,255,.8);border-radius:4px;cursor:pointer;z-index:20;opacity:0;pointer-events:none;transition:opacity .3s ease,background .2s,border-color .2s}.video-wrapper:hover .resize-handle[data-v-5d741303]{opacity:1;pointer-events:auto}.resize-handle[data-v-5d741303]:hover{background:#000000b3;border-color:#fff}.resize-handle.top-left[data-v-5d741303]{top:10px;left:10px;cursor:nwse-resize}.resize-handle.top-right[data-v-5d741303]{top:10px;right:10px;cursor:nesw-resize}.resize-handle.bottom-left[data-v-5d741303]{bottom:10px;left:10px;cursor:nesw-resize}.resize-handle.bottom-right[data-v-5d741303]{bottom:10px;right:10px;cursor:nwse-resize}.overlay[data-v-5d741303]{position:absolute;top:1rem;right:1rem;z-index:10}.status-badge[data-v-5d741303]{display:flex;align-items:center;gap:.5rem;padding:.25rem .75rem;border-radius:9999px;font-size:.75rem;font-weight:500;color:#fff;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.status-badge.paused[data-v-5d741303]{background:#ef4444cc}.status-badge.playing[data-v-5d741303]{background:#22c55ecc}.dot[data-v-5d741303]{width:6px;height:6px;border-radius:50%;background-color:#fff}@keyframes pulse-5d741303{0%,to{opacity:1}50%{opacity:.5}}.animate-pulse[data-v-5d741303]{animation:pulse-5d741303 2s cubic-bezier(.4,0,.6,1) infinite}
1
+ .fade-enter-active[data-v-6c9e435e],.fade-leave-active[data-v-6c9e435e]{transition:opacity .5s ease,transform .5s ease}.fade-enter-from[data-v-6c9e435e],.fade-leave-to[data-v-6c9e435e]{opacity:0;transform:translateY(10px)}.virtual-human-container[data-v-6c9e435e]{position:fixed;z-index:2147483647;overflow:visible}.video-wrapper[data-v-6c9e435e]{position:relative;width:100%;height:100%;border-radius:1rem;overflow:hidden;box-shadow:0 10px 25px #0000001a;background:#fff;border:2px solid transparent;transition:background-color .3s ease,box-shadow .3s ease,border-color .3s ease}.video-wrapper[data-v-6c9e435e]:hover{border-color:#409eff}.virtual-human-container.is-dark .video-wrapper[data-v-6c9e435e]{background:#1f2937;box-shadow:0 10px 25px #00000080}.persona-video[data-v-6c9e435e]{width:100%;height:100%;object-fit:cover;transform-origin:center;transition:transform .1s ease-out}.resize-handle[data-v-6c9e435e]{position:absolute;width:20px;height:20px;background:#00000080;border:2px solid rgba(255,255,255,.8);border-radius:4px;cursor:pointer;z-index:20;opacity:0;pointer-events:none;transition:opacity .3s ease,background .2s,border-color .2s}.video-wrapper:hover .resize-handle[data-v-6c9e435e]{opacity:1;pointer-events:auto}.resize-handle[data-v-6c9e435e]:hover{background:#000000b3;border-color:#fff}.resize-handle.top-left[data-v-6c9e435e]{top:10px;left:10px;cursor:nwse-resize}.resize-handle.top-right[data-v-6c9e435e]{top:10px;right:10px;cursor:nesw-resize}.resize-handle.bottom-left[data-v-6c9e435e]{bottom:10px;left:10px;cursor:nesw-resize}.resize-handle.bottom-right[data-v-6c9e435e]{bottom:10px;right:10px;cursor:nwse-resize}.overlay[data-v-6c9e435e]{position:absolute;top:1rem;right:1rem;z-index:10}.status-badge[data-v-6c9e435e]{display:flex;align-items:center;gap:.5rem;padding:.25rem .75rem;border-radius:9999px;font-size:.75rem;font-weight:500;color:#fff;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.status-badge.paused[data-v-6c9e435e]{background:#ef4444cc}.status-badge.playing[data-v-6c9e435e]{background:#22c55ecc}.dot[data-v-6c9e435e]{width:6px;height:6px;border-radius:50%;background-color:#fff}@keyframes pulse-6c9e435e{0%,to{opacity:1}50%{opacity:.5}}.animate-pulse[data-v-6c9e435e]{animation:pulse-6c9e435e 2s cubic-bezier(.4,0,.6,1) infinite}
@@ -1,4 +1,4 @@
1
- import { defineComponent as j, ref as d, watch as X, onMounted as G, onUnmounted as K, openBlock as P, createBlock as Q, Transition as Z, withCtx as _, createElementBlock as V, normalizeStyle as ee, normalizeClass as te, createElementVNode as R, createCommentVNode as T, createTextVNode as J, renderSlot as ne } from "vue";
1
+ import { defineComponent as j, ref as v, watch as X, onMounted as G, onUnmounted as K, openBlock as S, createBlock as Q, Transition as Z, withCtx as _, createElementBlock as E, normalizeStyle as ee, normalizeClass as te, createElementVNode as R, createCommentVNode as T, createTextVNode as J, renderSlot as ne } from "vue";
2
2
  const ae = ["src", "muted"], se = { class: "overlay" }, le = {
3
3
  key: 0,
4
4
  class: "status-badge paused"
@@ -46,72 +46,72 @@ const ae = ["src", "muted"], se = { class: "overlay" }, le = {
46
46
  }
47
47
  },
48
48
  emits: ["update:isPlaying", "ended", "update:visible"],
49
- setup(c, { emit: x }) {
50
- const t = c, l = x, u = d(null), Y = d(null), n = d(null), y = d(!1), h = d(400), w = d(500), W = d(16), E = d(16), z = d(!1), b = d(null), A = d(0), D = d(0), s = d(0), o = d(0), r = d(0), v = d(0), g = () => {
49
+ setup(c, { emit: W }) {
50
+ const t = c, i = W, r = v(null), Y = v(null), n = v(null), p = v(!1), y = v(400), g = v(500), w = v(16), z = v(16), I = v(!1), h = v(null), M = v(0), A = v(0), k = v(0), s = v(0), l = v(0), u = v(0), b = () => {
51
51
  if (n.value && n.value.close(), !(!t.wsUrl || !t.screenClientId))
52
52
  try {
53
- const i = new URL(t.wsUrl);
54
- i.searchParams.append("sessionId", t.screenClientId + "-persona"), n.value = new WebSocket(i.toString()), n.value.onopen = () => {
55
- y.value = !0, console.log(`[VirtualHumanPersona] Connected to ${t.wsUrl} for session ${t.screenClientId}-persona`);
53
+ const o = new URL(t.wsUrl);
54
+ o.searchParams.append("sessionId", t.screenClientId + "-persona"), n.value = new WebSocket(o.toString()), n.value.onopen = () => {
55
+ p.value = !0, console.log(`[VirtualHumanPersona] Connected to ${t.wsUrl} for session ${t.screenClientId}-persona`);
56
56
  }, n.value.onmessage = (e) => {
57
57
  try {
58
58
  const a = JSON.parse(e.data);
59
- B(a);
59
+ C(a);
60
60
  } catch (a) {
61
61
  console.error("[VirtualHumanPersona] Failed to parse message:", e.data, a);
62
62
  }
63
63
  }, n.value.onerror = (e) => {
64
64
  console.error("[VirtualHumanPersona] WebSocket error:", e);
65
65
  }, n.value.onclose = () => {
66
- y.value = !1, console.log("[VirtualHumanPersona] WebSocket disconnected");
66
+ p.value = !1, console.log("[VirtualHumanPersona] WebSocket disconnected");
67
67
  };
68
- } catch (i) {
69
- console.error("[VirtualHumanPersona] Failed to initialize WebSocket:", i);
68
+ } catch (o) {
69
+ console.error("[VirtualHumanPersona] Failed to initialize WebSocket:", o);
70
70
  }
71
- }, B = (i) => {
72
- const { type: e, action: a } = i;
73
- e === "control" && (a === "play" || a === "resume" ? (l("update:isPlaying", !0), l("update:visible", !0)) : a === "pause" ? l("update:isPlaying", !1) : a === "stop" && (l("update:isPlaying", !1), l("update:visible", !1), u.value && (u.value.currentTime = 0)));
71
+ }, C = (o) => {
72
+ const { type: e, action: a } = o;
73
+ e === "control" && (a === "play" || a === "resume" ? (i("update:isPlaying", !0), i("update:visible", !0)) : a === "pause" ? i("update:isPlaying", !1) : a === "stop" && (i("update:isPlaying", !1), i("update:visible", !1), r.value && (r.value.currentTime = 0)));
74
74
  };
75
75
  X(() => t.screenClientId, () => {
76
- t.screenClientId && t.wsUrl && g();
76
+ t.screenClientId && t.wsUrl && b();
77
77
  }), X(() => t.wsUrl, () => {
78
- t.screenClientId && t.wsUrl && g();
79
- }), X(() => t.isPlaying, (i) => {
80
- u.value && (i ? u.value.play().catch((e) => console.error("Video play failed:", e)) : u.value.pause());
78
+ t.screenClientId && t.wsUrl && b();
79
+ }), X(() => t.isPlaying, (o) => {
80
+ r.value && (o ? r.value.play().catch((e) => console.error("Video play failed:", e)) : r.value.pause());
81
81
  });
82
- const L = () => {
83
- t.isPlaying || l("update:isPlaying", !0);
82
+ const B = () => {
83
+ t.isPlaying || i("update:isPlaying", !0);
84
+ }, L = () => {
85
+ t.isPlaying && i("update:isPlaying", !1);
84
86
  }, U = () => {
85
- t.isPlaying && l("update:isPlaying", !1);
86
- }, f = () => {
87
- l("update:isPlaying", !1), l("ended");
88
- }, k = (i, e) => {
89
- e.preventDefault(), z.value = !0, b.value = i;
90
- const a = "clientX" in e ? e.clientX : e.touches[0].clientX, C = "clientY" in e ? e.clientY : e.touches[0].clientY;
91
- A.value = a, D.value = C, s.value = h.value, o.value = w.value, r.value = W.value, v.value = E.value, document.addEventListener("mousemove", M), document.addEventListener("mouseup", $), document.addEventListener("touchmove", M, { passive: !1 }), document.addEventListener("touchend", $);
92
- }, M = (i) => {
93
- if (!z.value) return;
94
- i.preventDefault();
95
- const e = "clientX" in i ? i.clientX : i.touches[0].clientX, a = "clientY" in i ? i.clientY : i.touches[0].clientY, C = e - A.value, I = a - D.value, F = 200, q = 250, N = 800, O = 1e3;
96
- let m = s.value, p = o.value, S = r.value, H = v.value;
97
- b.value === "bottom-right" ? (m = s.value + C, H = v.value - I, p = o.value + I) : b.value === "bottom-left" ? (S = r.value + C, m = s.value - C, H = v.value - I, p = o.value + I) : b.value === "top-right" ? (m = s.value + C, p = o.value - I) : b.value === "top-left" && (S = r.value + C, m = s.value - C, p = o.value - I), m < F && (S !== r.value && (S -= F - m), m = F), p < q && (H !== v.value && (H -= q - p), p = q), m > N && (S !== r.value && (S += m - N), m = N), p > O && (H !== v.value && (H += p - O), p = O), h.value = m, w.value = p, W.value = S, E.value = H;
98
- }, $ = () => {
99
- z.value = !1, b.value = null, document.removeEventListener("mousemove", M), document.removeEventListener("mouseup", $), document.removeEventListener("touchmove", M), document.removeEventListener("touchend", $);
87
+ i("update:isPlaying", !1), i("ended");
88
+ }, d = (o, e) => {
89
+ e.preventDefault(), I.value = !0, h.value = o;
90
+ const a = "clientX" in e ? e.clientX : e.touches[0].clientX, P = "clientY" in e ? e.clientY : e.touches[0].clientY;
91
+ M.value = a, A.value = P, k.value = y.value, s.value = g.value, l.value = w.value, u.value = z.value, document.addEventListener("mousemove", $), document.addEventListener("mouseup", N), document.addEventListener("touchmove", $, { passive: !1 }), document.addEventListener("touchend", N);
92
+ }, $ = (o) => {
93
+ if (!I.value) return;
94
+ o.preventDefault();
95
+ const e = "clientX" in o ? o.clientX : o.touches[0].clientX, a = "clientY" in o ? o.clientY : o.touches[0].clientY, P = e - M.value, x = a - A.value, D = 200, F = 250, q = 800, O = 1e3;
96
+ let f = k.value, m = s.value, H = l.value, V = u.value;
97
+ h.value === "bottom-right" ? (f = k.value + P, V = u.value - x, m = s.value + x) : h.value === "bottom-left" ? (H = l.value + P, f = k.value - P, V = u.value - x, m = s.value + x) : h.value === "top-right" ? (f = k.value + P, m = s.value - x) : h.value === "top-left" && (H = l.value + P, f = k.value - P, m = s.value - x), f < D && (H !== l.value && (H -= D - f), f = D), m < F && (V !== u.value && (V -= F - m), m = F), f > q && (H !== l.value && (H += f - q), f = q), m > O && (V !== u.value && (V += m - O), m = O), y.value = f, g.value = m, w.value = H, z.value = V;
98
+ }, N = () => {
99
+ I.value = !1, h.value = null, document.removeEventListener("mousemove", $), document.removeEventListener("mouseup", N), document.removeEventListener("touchmove", $), document.removeEventListener("touchend", N);
100
100
  };
101
101
  return G(() => {
102
- t.isPlaying && u.value && u.value.play().catch((i) => console.error("Video play failed:", i)), t.screenClientId && t.wsUrl && g();
102
+ t.isPlaying && r.value && r.value.play().catch((o) => console.error("Video play failed:", o)), t.screenClientId && t.wsUrl && b();
103
103
  }), K(() => {
104
104
  n.value && n.value.close();
105
- }), (i, e) => (P(), Q(Z, { name: "fade" }, {
105
+ }), (o, e) => (S(), Q(Z, { name: "fade" }, {
106
106
  default: _(() => [
107
- c.visible ? (P(), V("div", {
107
+ c.visible ? (S(), E("div", {
108
108
  key: 0,
109
109
  class: te(["virtual-human-container", { "is-dark": c.isDark }]),
110
110
  style: ee({
111
- width: h.value + "px",
112
- height: w.value + "px",
113
- left: W.value + "px",
114
- bottom: E.value + "px"
111
+ width: y.value + "px",
112
+ height: g.value + "px",
113
+ left: w.value + "px",
114
+ bottom: z.value + "px"
115
115
  })
116
116
  }, [
117
117
  R("div", {
@@ -121,45 +121,47 @@ const ae = ["src", "muted"], se = { class: "overlay" }, le = {
121
121
  }, [
122
122
  R("video", {
123
123
  ref_key: "videoRef",
124
- ref: u,
124
+ ref: r,
125
125
  src: c.videoSrc,
126
126
  class: "persona-video",
127
127
  muted: c.muted,
128
128
  playsinline: "",
129
129
  loop: "",
130
- onPlay: L,
131
- onPause: U,
132
- onEnded: f
130
+ autoPlay: "",
131
+ disablePictureInPicture: "false",
132
+ onPlay: B,
133
+ onPause: L,
134
+ onEnded: U
133
135
  }, null, 40, ae),
134
- c.visible ? (P(), V("div", {
136
+ c.visible ? (S(), E("div", {
135
137
  key: 0,
136
138
  class: "resize-handle top-left",
137
- onMousedown: e[0] || (e[0] = (a) => k("top-left", a)),
138
- onTouchstart: e[1] || (e[1] = (a) => k("top-left", a))
139
+ onMousedown: e[0] || (e[0] = (a) => d("top-left", a)),
140
+ onTouchstart: e[1] || (e[1] = (a) => d("top-left", a))
139
141
  }, null, 32)) : T("", !0),
140
- c.visible ? (P(), V("div", {
142
+ c.visible ? (S(), E("div", {
141
143
  key: 1,
142
144
  class: "resize-handle top-right",
143
- onMousedown: e[2] || (e[2] = (a) => k("top-right", a)),
144
- onTouchstart: e[3] || (e[3] = (a) => k("top-right", a))
145
+ onMousedown: e[2] || (e[2] = (a) => d("top-right", a)),
146
+ onTouchstart: e[3] || (e[3] = (a) => d("top-right", a))
145
147
  }, null, 32)) : T("", !0),
146
- c.visible ? (P(), V("div", {
148
+ c.visible ? (S(), E("div", {
147
149
  key: 2,
148
150
  class: "resize-handle bottom-left",
149
- onMousedown: e[4] || (e[4] = (a) => k("bottom-left", a)),
150
- onTouchstart: e[5] || (e[5] = (a) => k("bottom-left", a))
151
+ onMousedown: e[4] || (e[4] = (a) => d("bottom-left", a)),
152
+ onTouchstart: e[5] || (e[5] = (a) => d("bottom-left", a))
151
153
  }, null, 32)) : T("", !0),
152
- c.visible ? (P(), V("div", {
154
+ c.visible ? (S(), E("div", {
153
155
  key: 3,
154
156
  class: "resize-handle bottom-right",
155
- onMousedown: e[6] || (e[6] = (a) => k("bottom-right", a)),
156
- onTouchstart: e[7] || (e[7] = (a) => k("bottom-right", a))
157
+ onMousedown: e[6] || (e[6] = (a) => d("bottom-right", a)),
158
+ onTouchstart: e[7] || (e[7] = (a) => d("bottom-right", a))
157
159
  }, null, 32)) : T("", !0),
158
160
  R("div", se, [
159
- c.isPlaying ? (P(), V("div", oe, [...e[9] || (e[9] = [
161
+ c.isPlaying ? (S(), E("div", oe, [...e[9] || (e[9] = [
160
162
  R("span", { class: "dot animate-pulse" }, null, -1),
161
163
  J(" 播放中 ", -1)
162
- ])])) : (P(), V("div", le, [...e[8] || (e[8] = [
164
+ ])])) : (S(), E("div", le, [...e[8] || (e[8] = [
163
165
  R("span", { class: "dot" }, null, -1),
164
166
  J(" 暂停中 ", -1)
165
167
  ])]))
@@ -170,12 +172,12 @@ const ae = ["src", "muted"], se = { class: "overlay" }, le = {
170
172
  _: 1
171
173
  }));
172
174
  }
173
- }), ie = (c, x) => {
175
+ }), ie = (c, W) => {
174
176
  const t = c.__vccOpts || c;
175
- for (const [l, u] of x)
176
- t[l] = u;
177
+ for (const [i, r] of W)
178
+ t[i] = r;
177
179
  return t;
178
- }, ue = /* @__PURE__ */ ie(re, [["__scopeId", "data-v-5d741303"]]), ce = /* @__PURE__ */ j({
180
+ }, ue = /* @__PURE__ */ ie(re, [["__scopeId", "data-v-6c9e435e"]]), ce = /* @__PURE__ */ j({
179
181
  __name: "VirtualHumanEventAdapter",
180
182
  props: {
181
183
  // 屏幕客户端ID
@@ -189,103 +191,96 @@ const ae = ["src", "muted"], se = { class: "overlay" }, le = {
189
191
  required: !0
190
192
  }
191
193
  },
192
- emits: ["highlight", "showDialog", "end", "pause", "connected", "error", "play_complete"],
193
- setup(c, { emit: x }) {
194
- const t = c, l = x, u = d(null), Y = d(!1);
195
- let n = null, y = 0, h = !1, w = 0;
196
- const W = () => {
194
+ emits: ["eventNotifaction", "end", "pause", "connected", "error", "playComplete"],
195
+ setup(c, { emit: W }) {
196
+ const t = c, i = W, r = v(null), Y = v(!1);
197
+ let n = null, p = 0, y = !1, g = 0, w = !1;
198
+ const z = () => {
197
199
  n || (n = new (window.AudioContext || window.webkitAudioContext)({
198
200
  sampleRate: 24e3
199
- })), n.state === "suspended" && n.resume();
200
- }, E = () => {
201
- h && w === 0 && (l("play_complete", t.screenClientId), h = !1);
202
- }, z = (s) => {
203
- if (W(), !!n)
201
+ })), n.state === "suspended" && !w && n.resume();
202
+ }, I = () => {
203
+ y && g === 0 && (i("playComplete", t.screenClientId), y = !1);
204
+ }, h = (s) => {
205
+ if (z(), !!n)
204
206
  try {
205
- const o = window.atob(s), r = o.length, v = new Uint8Array(r);
206
- for (let f = 0; f < r; f++)
207
- v[f] = o.charCodeAt(f);
208
- const g = new Int16Array(v.buffer), B = new Float32Array(g.length);
209
- for (let f = 0; f < g.length; f++)
210
- B[f] = g[f] / 32768;
207
+ const l = window.atob(s), u = l.length, b = new Uint8Array(u);
208
+ for (let d = 0; d < u; d++)
209
+ b[d] = l.charCodeAt(d);
210
+ const C = new Int16Array(b.buffer), B = new Float32Array(C.length);
211
+ for (let d = 0; d < C.length; d++)
212
+ B[d] = C[d] / 32768;
211
213
  const L = n.createBuffer(1, B.length, 24e3);
212
214
  L.getChannelData(0).set(B);
213
215
  const U = n.createBufferSource();
214
- U.buffer = L, U.connect(n.destination), y < n.currentTime && (y = n.currentTime), U.start(y), y += L.duration, w++, U.onended = () => {
215
- w--, E();
216
+ U.buffer = L, U.connect(n.destination), p < n.currentTime && (p = n.currentTime), U.start(p), p += L.duration, g++, U.onended = () => {
217
+ g--, I();
216
218
  };
217
- } catch (o) {
218
- console.error("[VirtualHumanEventAdapter] Failed to decode and play audio:", o);
219
- }
220
- }, b = (s) => {
221
- if (!(!n && s !== "stop"))
222
- switch (s) {
223
- case "play":
224
- h = !1, w = 0, y = 0, n && n.state === "suspended" && n.resume();
225
- break;
226
- case "resume":
227
- n && n.state === "suspended" && n.resume();
228
- break;
229
- case "pause":
230
- n && n.state === "running" && n.suspend();
231
- break;
232
- case "stop":
233
- n && (n.close(), n = null), y = 0, h = !1, w = 0;
234
- break;
235
- case "tts_complete":
236
- h = !0, E();
237
- break;
238
- default:
239
- console.warn(`[VirtualHumanEventAdapter] Unknown control action: ${s}`);
219
+ } catch (l) {
220
+ console.error("[VirtualHumanEventAdapter] Failed to decode and play audio:", l);
240
221
  }
222
+ }, M = (s) => {
223
+ switch (s) {
224
+ case "play":
225
+ y = !1, g = 0, p = 0, w = !1, n && n.state === "suspended" && n.resume();
226
+ break;
227
+ case "resume":
228
+ w = !1, n && n.state === "suspended" && n.resume();
229
+ break;
230
+ case "pause":
231
+ w = !0, n && n.state === "running" && n.suspend();
232
+ break;
233
+ case "stop":
234
+ w = !1, n && (n.close(), n = null), p = 0, y = !1, g = 0;
235
+ break;
236
+ case "tts_complete":
237
+ y = !0, I();
238
+ break;
239
+ default:
240
+ console.warn(`[VirtualHumanEventAdapter] Unknown control action: ${s}`);
241
+ }
241
242
  }, A = () => {
242
- u.value && u.value.close();
243
+ r.value && r.value.close();
243
244
  try {
244
245
  const s = new URL(t.wsUrl);
245
- s.searchParams.append("sessionId", t.screenClientId + "-event"), u.value = new WebSocket(s.toString()), u.value.onopen = () => {
246
- Y.value = !0, l("connected"), console.log(`[VirtualHumanEventAdapter] Connected to ${t.wsUrl} for session ${t.screenClientId}-event`);
247
- }, u.value.onmessage = (o) => {
246
+ s.searchParams.append("sessionId", t.screenClientId + "-event"), r.value = new WebSocket(s.toString()), r.value.onopen = () => {
247
+ Y.value = !0, i("connected"), console.log(`[VirtualHumanEventAdapter] Connected to ${t.wsUrl} for session ${t.screenClientId}-event`);
248
+ }, r.value.onmessage = (l) => {
248
249
  try {
249
- const r = JSON.parse(o.data);
250
- D(r);
251
- } catch (r) {
252
- console.error("[VirtualHumanEventAdapter] Failed to parse message:", o.data, r);
250
+ const u = JSON.parse(l.data);
251
+ k(u);
252
+ } catch (u) {
253
+ console.error("[VirtualHumanEventAdapter] Failed to parse message:", l.data, u);
253
254
  }
254
- }, u.value.onerror = (o) => {
255
- console.error("[VirtualHumanEventAdapter] WebSocket error:", o), l("error", o);
256
- }, u.value.onclose = () => {
255
+ }, r.value.onerror = (l) => {
256
+ console.error("[VirtualHumanEventAdapter] WebSocket error:", l), i("error", l);
257
+ }, r.value.onclose = () => {
257
258
  Y.value = !1, console.log("[VirtualHumanEventAdapter] WebSocket disconnected");
258
259
  };
259
260
  } catch (s) {
260
- console.error("[VirtualHumanEventAdapter] Failed to initialize WebSocket:", s), l("error", s);
261
+ console.error("[VirtualHumanEventAdapter] Failed to initialize WebSocket:", s), i("error", s);
261
262
  }
262
- }, D = (s) => {
263
- const { type: o, payload: r, action: v } = s;
264
- switch (console.log("msgmsg", s), o) {
263
+ }, k = (s) => {
264
+ const { type: l, payload: u, action: b } = s;
265
+ switch (console.log("msgmsg-002", s), l) {
265
266
  case "audio":
266
- const g = (r == null ? void 0 : r.data) || s.data;
267
- g && z(g);
267
+ const C = (u == null ? void 0 : u.data) || s.data;
268
+ C && h(C);
268
269
  break;
269
- case "dialog_event":
270
- s.event && l(s.event, s.params);
270
+ case "send_event":
271
+ s.event && i("eventNotifaction", s);
271
272
  break;
272
273
  case "control":
273
- v && b(v);
274
- break;
275
- case "highlight":
276
- l("highlight", r);
277
- break;
278
- case "showDialog":
279
- l("showDialog", r);
274
+ b && M(b);
280
275
  break;
281
276
  case "end":
282
- l("end", r);
277
+ i("end", u);
283
278
  break;
284
279
  case "pause":
285
- l("pause", r);
280
+ i("pause", u);
286
281
  break;
287
282
  default:
288
- console.warn(`[VirtualHumanEventAdapter] Unknown message type: ${o}`);
283
+ console.warn(`[VirtualHumanEventAdapter] Unknown message type: ${l}`);
289
284
  }
290
285
  };
291
286
  return X(() => t.screenClientId, () => {
@@ -295,8 +290,8 @@ const ae = ["src", "muted"], se = { class: "overlay" }, le = {
295
290
  }), G(() => {
296
291
  t.screenClientId && t.wsUrl && A();
297
292
  }), K(() => {
298
- u.value && u.value.close(), n && n.close();
299
- }), (s, o) => ne(s.$slots, "default");
293
+ r.value && r.value.close(), n && n.close();
294
+ }), (s, l) => ne(s.$slots, "default");
300
295
  }
301
296
  }), de = (c) => {
302
297
  c.component("VirtualHumanPersona", ue), c.component("VirtualHumanEventAdapter", ce);
@@ -1 +1 @@
1
- (function(h,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(h=typeof globalThis<"u"?globalThis:h||self,e(h.VirtualHumanCf={},h.Vue))})(this,function(h,e){"use strict";const F=["src","muted"],O={class:"overlay"},j={key:0,class:"status-badge paused"},J={key:1,class:"status-badge playing"},$=((d,A)=>{const n=d.__vccOpts||d;for(const[s,u]of A)n[s]=u;return n})(e.defineComponent({__name:"VirtualHumanPersona",props:{videoSrc:{type:String,required:!0},visible:{type:Boolean,default:!1},isPlaying:{type:Boolean,default:!1},muted:{type:Boolean,default:!0},isDark:{type:Boolean,default:!1},screenClientId:{type:String,required:!1},wsUrl:{type:String,required:!1}},emits:["update:isPlaying","ended","update:visible"],setup(d,{emit:A}){const n=d,s=A,u=e.ref(null),N=e.ref(null),a=e.ref(null),y=e.ref(!1),w=e.ref(400),k=e.ref(500),I=e.ref(16),S=e.ref(16),x=e.ref(!1),b=e.ref(null),H=e.ref(0),z=e.ref(0),l=e.ref(0),r=e.ref(0),i=e.ref(0),f=e.ref(0),g=()=>{if(a.value&&a.value.close(),!(!n.wsUrl||!n.screenClientId))try{const c=new URL(n.wsUrl);c.searchParams.append("sessionId",n.screenClientId+"-persona"),a.value=new WebSocket(c.toString()),a.value.onopen=()=>{y.value=!0,console.log(`[VirtualHumanPersona] Connected to ${n.wsUrl} for session ${n.screenClientId}-persona`)},a.value.onmessage=t=>{try{const o=JSON.parse(t.data);T(o)}catch(o){console.error("[VirtualHumanPersona] Failed to parse message:",t.data,o)}},a.value.onerror=t=>{console.error("[VirtualHumanPersona] WebSocket error:",t)},a.value.onclose=()=>{y.value=!1,console.log("[VirtualHumanPersona] WebSocket disconnected")}}catch(c){console.error("[VirtualHumanPersona] Failed to initialize WebSocket:",c)}},T=c=>{const{type:t,action:o}=c;t==="control"&&(o==="play"||o==="resume"?(s("update:isPlaying",!0),s("update:visible",!0)):o==="pause"?s("update:isPlaying",!1):o==="stop"&&(s("update:isPlaying",!1),s("update:visible",!1),u.value&&(u.value.currentTime=0)))};e.watch(()=>n.screenClientId,()=>{n.screenClientId&&n.wsUrl&&g()}),e.watch(()=>n.wsUrl,()=>{n.screenClientId&&n.wsUrl&&g()}),e.watch(()=>n.isPlaying,c=>{u.value&&(c?u.value.play().catch(t=>console.error("Video play failed:",t)):u.value.pause())});const W=()=>{n.isPlaying||s("update:isPlaying",!0)},B=()=>{n.isPlaying&&s("update:isPlaying",!1)},m=()=>{s("update:isPlaying",!1),s("ended")},C=(c,t)=>{t.preventDefault(),x.value=!0,b.value=c;const o="clientX"in t?t.clientX:t.touches[0].clientX,V="clientY"in t?t.clientY:t.touches[0].clientY;H.value=o,z.value=V,l.value=w.value,r.value=k.value,i.value=I.value,f.value=S.value,document.addEventListener("mousemove",L),document.addEventListener("mouseup",M),document.addEventListener("touchmove",L,{passive:!1}),document.addEventListener("touchend",M)},L=c=>{if(!x.value)return;c.preventDefault();const t="clientX"in c?c.clientX:c.touches[0].clientX,o="clientY"in c?c.clientY:c.touches[0].clientY,V=t-H.value,U=o-z.value,R=200,X=250,Y=800,D=1e3;let p=l.value,v=r.value,E=i.value,P=f.value;b.value==="bottom-right"?(p=l.value+V,P=f.value-U,v=r.value+U):b.value==="bottom-left"?(E=i.value+V,p=l.value-V,P=f.value-U,v=r.value+U):b.value==="top-right"?(p=l.value+V,v=r.value-U):b.value==="top-left"&&(E=i.value+V,p=l.value-V,v=r.value-U),p<R&&(E!==i.value&&(E-=R-p),p=R),v<X&&(P!==f.value&&(P-=X-v),v=X),p>Y&&(E!==i.value&&(E+=p-Y),p=Y),v>D&&(P!==f.value&&(P+=v-D),v=D),w.value=p,k.value=v,I.value=E,S.value=P},M=()=>{x.value=!1,b.value=null,document.removeEventListener("mousemove",L),document.removeEventListener("mouseup",M),document.removeEventListener("touchmove",L),document.removeEventListener("touchend",M)};return e.onMounted(()=>{n.isPlaying&&u.value&&u.value.play().catch(c=>console.error("Video play failed:",c)),n.screenClientId&&n.wsUrl&&g()}),e.onUnmounted(()=>{a.value&&a.value.close()}),(c,t)=>(e.openBlock(),e.createBlock(e.Transition,{name:"fade"},{default:e.withCtx(()=>[d.visible?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(["virtual-human-container",{"is-dark":d.isDark}]),style:e.normalizeStyle({width:w.value+"px",height:k.value+"px",left:I.value+"px",bottom:S.value+"px"})},[e.createElementVNode("div",{class:"video-wrapper",ref_key:"wrapperRef",ref:N},[e.createElementVNode("video",{ref_key:"videoRef",ref:u,src:d.videoSrc,class:"persona-video",muted:d.muted,playsinline:"",loop:"",onPlay:W,onPause:B,onEnded:m},null,40,F),d.visible?(e.openBlock(),e.createElementBlock("div",{key:0,class:"resize-handle top-left",onMousedown:t[0]||(t[0]=o=>C("top-left",o)),onTouchstart:t[1]||(t[1]=o=>C("top-left",o))},null,32)):e.createCommentVNode("",!0),d.visible?(e.openBlock(),e.createElementBlock("div",{key:1,class:"resize-handle top-right",onMousedown:t[2]||(t[2]=o=>C("top-right",o)),onTouchstart:t[3]||(t[3]=o=>C("top-right",o))},null,32)):e.createCommentVNode("",!0),d.visible?(e.openBlock(),e.createElementBlock("div",{key:2,class:"resize-handle bottom-left",onMousedown:t[4]||(t[4]=o=>C("bottom-left",o)),onTouchstart:t[5]||(t[5]=o=>C("bottom-left",o))},null,32)):e.createCommentVNode("",!0),d.visible?(e.openBlock(),e.createElementBlock("div",{key:3,class:"resize-handle bottom-right",onMousedown:t[6]||(t[6]=o=>C("bottom-right",o)),onTouchstart:t[7]||(t[7]=o=>C("bottom-right",o))},null,32)):e.createCommentVNode("",!0),e.createElementVNode("div",O,[d.isPlaying?(e.openBlock(),e.createElementBlock("div",J,[...t[9]||(t[9]=[e.createElementVNode("span",{class:"dot animate-pulse"},null,-1),e.createTextVNode(" 播放中 ",-1)])])):(e.openBlock(),e.createElementBlock("div",j,[...t[8]||(t[8]=[e.createElementVNode("span",{class:"dot"},null,-1),e.createTextVNode(" 暂停中 ",-1)])]))])],512)],6)):e.createCommentVNode("",!0)]),_:1}))}}),[["__scopeId","data-v-5d741303"]]),q=e.defineComponent({__name:"VirtualHumanEventAdapter",props:{screenClientId:{type:String,required:!0},wsUrl:{type:String,required:!0}},emits:["highlight","showDialog","end","pause","connected","error","play_complete"],setup(d,{emit:A}){const n=d,s=A,u=e.ref(null),N=e.ref(!1);let a=null,y=0,w=!1,k=0;const I=()=>{a||(a=new(window.AudioContext||window.webkitAudioContext)({sampleRate:24e3})),a.state==="suspended"&&a.resume()},S=()=>{w&&k===0&&(s("play_complete",n.screenClientId),w=!1)},x=l=>{if(I(),!!a)try{const r=window.atob(l),i=r.length,f=new Uint8Array(i);for(let m=0;m<i;m++)f[m]=r.charCodeAt(m);const g=new Int16Array(f.buffer),T=new Float32Array(g.length);for(let m=0;m<g.length;m++)T[m]=g[m]/32768;const W=a.createBuffer(1,T.length,24e3);W.getChannelData(0).set(T);const B=a.createBufferSource();B.buffer=W,B.connect(a.destination),y<a.currentTime&&(y=a.currentTime),B.start(y),y+=W.duration,k++,B.onended=()=>{k--,S()}}catch(r){console.error("[VirtualHumanEventAdapter] Failed to decode and play audio:",r)}},b=l=>{if(!(!a&&l!=="stop"))switch(l){case"play":w=!1,k=0,y=0,a&&a.state==="suspended"&&a.resume();break;case"resume":a&&a.state==="suspended"&&a.resume();break;case"pause":a&&a.state==="running"&&a.suspend();break;case"stop":a&&(a.close(),a=null),y=0,w=!1,k=0;break;case"tts_complete":w=!0,S();break;default:console.warn(`[VirtualHumanEventAdapter] Unknown control action: ${l}`)}},H=()=>{u.value&&u.value.close();try{const l=new URL(n.wsUrl);l.searchParams.append("sessionId",n.screenClientId+"-event"),u.value=new WebSocket(l.toString()),u.value.onopen=()=>{N.value=!0,s("connected"),console.log(`[VirtualHumanEventAdapter] Connected to ${n.wsUrl} for session ${n.screenClientId}-event`)},u.value.onmessage=r=>{try{const i=JSON.parse(r.data);z(i)}catch(i){console.error("[VirtualHumanEventAdapter] Failed to parse message:",r.data,i)}},u.value.onerror=r=>{console.error("[VirtualHumanEventAdapter] WebSocket error:",r),s("error",r)},u.value.onclose=()=>{N.value=!1,console.log("[VirtualHumanEventAdapter] WebSocket disconnected")}}catch(l){console.error("[VirtualHumanEventAdapter] Failed to initialize WebSocket:",l),s("error",l)}},z=l=>{const{type:r,payload:i,action:f}=l;switch(console.log("msgmsg",l),r){case"audio":const g=(i==null?void 0:i.data)||l.data;g&&x(g);break;case"dialog_event":l.event&&s(l.event,l.params);break;case"control":f&&b(f);break;case"highlight":s("highlight",i);break;case"showDialog":s("showDialog",i);break;case"end":s("end",i);break;case"pause":s("pause",i);break;default:console.warn(`[VirtualHumanEventAdapter] Unknown message type: ${r}`)}};return e.watch(()=>n.screenClientId,()=>{n.screenClientId&&n.wsUrl&&H()}),e.watch(()=>n.wsUrl,()=>{n.screenClientId&&n.wsUrl&&H()}),e.onMounted(()=>{n.screenClientId&&n.wsUrl&&H()}),e.onUnmounted(()=>{u.value&&u.value.close(),a&&a.close()}),(l,r)=>e.renderSlot(l.$slots,"default")}}),G={install:d=>{d.component("VirtualHumanPersona",$),d.component("VirtualHumanEventAdapter",q)}};h.VirtualHumanEventAdapter=q,h.VirtualHumanPersona=$,h.default=G,Object.defineProperties(h,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
1
+ (function(v,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(v=typeof globalThis<"u"?globalThis:v||self,e(v.VirtualHumanCf={},v.Vue))})(this,function(v,e){"use strict";const F=["src","muted"],O={class:"overlay"},j={key:0,class:"status-badge paused"},J={key:1,class:"status-badge playing"},q=((d,A)=>{const n=d.__vccOpts||d;for(const[c,i]of A)n[c]=i;return n})(e.defineComponent({__name:"VirtualHumanPersona",props:{videoSrc:{type:String,required:!0},visible:{type:Boolean,default:!1},isPlaying:{type:Boolean,default:!1},muted:{type:Boolean,default:!0},isDark:{type:Boolean,default:!1},screenClientId:{type:String,required:!1},wsUrl:{type:String,required:!1}},emits:["update:isPlaying","ended","update:visible"],setup(d,{emit:A}){const n=d,c=A,i=e.ref(null),W=e.ref(null),a=e.ref(null),y=e.ref(!1),g=e.ref(400),w=e.ref(500),h=e.ref(16),x=e.ref(16),H=e.ref(!1),b=e.ref(null),z=e.ref(0),B=e.ref(0),C=e.ref(0),l=e.ref(0),s=e.ref(0),u=e.ref(0),k=()=>{if(a.value&&a.value.close(),!(!n.wsUrl||!n.screenClientId))try{const r=new URL(n.wsUrl);r.searchParams.append("sessionId",n.screenClientId+"-persona"),a.value=new WebSocket(r.toString()),a.value.onopen=()=>{y.value=!0,console.log(`[VirtualHumanPersona] Connected to ${n.wsUrl} for session ${n.screenClientId}-persona`)},a.value.onmessage=t=>{try{const o=JSON.parse(t.data);V(o)}catch(o){console.error("[VirtualHumanPersona] Failed to parse message:",t.data,o)}},a.value.onerror=t=>{console.error("[VirtualHumanPersona] WebSocket error:",t)},a.value.onclose=()=>{y.value=!1,console.log("[VirtualHumanPersona] WebSocket disconnected")}}catch(r){console.error("[VirtualHumanPersona] Failed to initialize WebSocket:",r)}},V=r=>{const{type:t,action:o}=r;t==="control"&&(o==="play"||o==="resume"?(c("update:isPlaying",!0),c("update:visible",!0)):o==="pause"?c("update:isPlaying",!1):o==="stop"&&(c("update:isPlaying",!1),c("update:visible",!1),i.value&&(i.value.currentTime=0)))};e.watch(()=>n.screenClientId,()=>{n.screenClientId&&n.wsUrl&&k()}),e.watch(()=>n.wsUrl,()=>{n.screenClientId&&n.wsUrl&&k()}),e.watch(()=>n.isPlaying,r=>{i.value&&(r?i.value.play().catch(t=>console.error("Video play failed:",t)):i.value.pause())});const N=()=>{n.isPlaying||c("update:isPlaying",!0)},T=()=>{n.isPlaying&&c("update:isPlaying",!1)},I=()=>{c("update:isPlaying",!1),c("ended")},f=(r,t)=>{t.preventDefault(),H.value=!0,b.value=r;const o="clientX"in t?t.clientX:t.touches[0].clientX,P="clientY"in t?t.clientY:t.touches[0].clientY;z.value=o,B.value=P,C.value=g.value,l.value=w.value,s.value=h.value,u.value=x.value,document.addEventListener("mousemove",L),document.addEventListener("mouseup",M),document.addEventListener("touchmove",L,{passive:!1}),document.addEventListener("touchend",M)},L=r=>{if(!H.value)return;r.preventDefault();const t="clientX"in r?r.clientX:r.touches[0].clientX,o="clientY"in r?r.clientY:r.touches[0].clientY,P=t-z.value,U=o-B.value,R=200,X=250,Y=800,$=1e3;let m=C.value,p=l.value,E=s.value,S=u.value;b.value==="bottom-right"?(m=C.value+P,S=u.value-U,p=l.value+U):b.value==="bottom-left"?(E=s.value+P,m=C.value-P,S=u.value-U,p=l.value+U):b.value==="top-right"?(m=C.value+P,p=l.value-U):b.value==="top-left"&&(E=s.value+P,m=C.value-P,p=l.value-U),m<R&&(E!==s.value&&(E-=R-m),m=R),p<X&&(S!==u.value&&(S-=X-p),p=X),m>Y&&(E!==s.value&&(E+=m-Y),m=Y),p>$&&(S!==u.value&&(S+=p-$),p=$),g.value=m,w.value=p,h.value=E,x.value=S},M=()=>{H.value=!1,b.value=null,document.removeEventListener("mousemove",L),document.removeEventListener("mouseup",M),document.removeEventListener("touchmove",L),document.removeEventListener("touchend",M)};return e.onMounted(()=>{n.isPlaying&&i.value&&i.value.play().catch(r=>console.error("Video play failed:",r)),n.screenClientId&&n.wsUrl&&k()}),e.onUnmounted(()=>{a.value&&a.value.close()}),(r,t)=>(e.openBlock(),e.createBlock(e.Transition,{name:"fade"},{default:e.withCtx(()=>[d.visible?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(["virtual-human-container",{"is-dark":d.isDark}]),style:e.normalizeStyle({width:g.value+"px",height:w.value+"px",left:h.value+"px",bottom:x.value+"px"})},[e.createElementVNode("div",{class:"video-wrapper",ref_key:"wrapperRef",ref:W},[e.createElementVNode("video",{ref_key:"videoRef",ref:i,src:d.videoSrc,class:"persona-video",muted:d.muted,playsinline:"",loop:"",autoPlay:"",disablePictureInPicture:"false",onPlay:N,onPause:T,onEnded:I},null,40,F),d.visible?(e.openBlock(),e.createElementBlock("div",{key:0,class:"resize-handle top-left",onMousedown:t[0]||(t[0]=o=>f("top-left",o)),onTouchstart:t[1]||(t[1]=o=>f("top-left",o))},null,32)):e.createCommentVNode("",!0),d.visible?(e.openBlock(),e.createElementBlock("div",{key:1,class:"resize-handle top-right",onMousedown:t[2]||(t[2]=o=>f("top-right",o)),onTouchstart:t[3]||(t[3]=o=>f("top-right",o))},null,32)):e.createCommentVNode("",!0),d.visible?(e.openBlock(),e.createElementBlock("div",{key:2,class:"resize-handle bottom-left",onMousedown:t[4]||(t[4]=o=>f("bottom-left",o)),onTouchstart:t[5]||(t[5]=o=>f("bottom-left",o))},null,32)):e.createCommentVNode("",!0),d.visible?(e.openBlock(),e.createElementBlock("div",{key:3,class:"resize-handle bottom-right",onMousedown:t[6]||(t[6]=o=>f("bottom-right",o)),onTouchstart:t[7]||(t[7]=o=>f("bottom-right",o))},null,32)):e.createCommentVNode("",!0),e.createElementVNode("div",O,[d.isPlaying?(e.openBlock(),e.createElementBlock("div",J,[...t[9]||(t[9]=[e.createElementVNode("span",{class:"dot animate-pulse"},null,-1),e.createTextVNode(" 播放中 ",-1)])])):(e.openBlock(),e.createElementBlock("div",j,[...t[8]||(t[8]=[e.createElementVNode("span",{class:"dot"},null,-1),e.createTextVNode(" 暂停中 ",-1)])]))])],512)],6)):e.createCommentVNode("",!0)]),_:1}))}}),[["__scopeId","data-v-6c9e435e"]]),D=e.defineComponent({__name:"VirtualHumanEventAdapter",props:{screenClientId:{type:String,required:!0},wsUrl:{type:String,required:!0}},emits:["eventNotifaction","end","pause","connected","error","playComplete"],setup(d,{emit:A}){const n=d,c=A,i=e.ref(null),W=e.ref(!1);let a=null,y=0,g=!1,w=0,h=!1;const x=()=>{a||(a=new(window.AudioContext||window.webkitAudioContext)({sampleRate:24e3})),a.state==="suspended"&&!h&&a.resume()},H=()=>{g&&w===0&&(c("playComplete",n.screenClientId),g=!1)},b=l=>{if(x(),!!a)try{const s=window.atob(l),u=s.length,k=new Uint8Array(u);for(let f=0;f<u;f++)k[f]=s.charCodeAt(f);const V=new Int16Array(k.buffer),N=new Float32Array(V.length);for(let f=0;f<V.length;f++)N[f]=V[f]/32768;const T=a.createBuffer(1,N.length,24e3);T.getChannelData(0).set(N);const I=a.createBufferSource();I.buffer=T,I.connect(a.destination),y<a.currentTime&&(y=a.currentTime),I.start(y),y+=T.duration,w++,I.onended=()=>{w--,H()}}catch(s){console.error("[VirtualHumanEventAdapter] Failed to decode and play audio:",s)}},z=l=>{switch(l){case"play":g=!1,w=0,y=0,h=!1,a&&a.state==="suspended"&&a.resume();break;case"resume":h=!1,a&&a.state==="suspended"&&a.resume();break;case"pause":h=!0,a&&a.state==="running"&&a.suspend();break;case"stop":h=!1,a&&(a.close(),a=null),y=0,g=!1,w=0;break;case"tts_complete":g=!0,H();break;default:console.warn(`[VirtualHumanEventAdapter] Unknown control action: ${l}`)}},B=()=>{i.value&&i.value.close();try{const l=new URL(n.wsUrl);l.searchParams.append("sessionId",n.screenClientId+"-event"),i.value=new WebSocket(l.toString()),i.value.onopen=()=>{W.value=!0,c("connected"),console.log(`[VirtualHumanEventAdapter] Connected to ${n.wsUrl} for session ${n.screenClientId}-event`)},i.value.onmessage=s=>{try{const u=JSON.parse(s.data);C(u)}catch(u){console.error("[VirtualHumanEventAdapter] Failed to parse message:",s.data,u)}},i.value.onerror=s=>{console.error("[VirtualHumanEventAdapter] WebSocket error:",s),c("error",s)},i.value.onclose=()=>{W.value=!1,console.log("[VirtualHumanEventAdapter] WebSocket disconnected")}}catch(l){console.error("[VirtualHumanEventAdapter] Failed to initialize WebSocket:",l),c("error",l)}},C=l=>{const{type:s,payload:u,action:k}=l;switch(console.log("msgmsg-002",l),s){case"audio":const V=(u==null?void 0:u.data)||l.data;V&&b(V);break;case"send_event":l.event&&c("eventNotifaction",l);break;case"control":k&&z(k);break;case"end":c("end",u);break;case"pause":c("pause",u);break;default:console.warn(`[VirtualHumanEventAdapter] Unknown message type: ${s}`)}};return e.watch(()=>n.screenClientId,()=>{n.screenClientId&&n.wsUrl&&B()}),e.watch(()=>n.wsUrl,()=>{n.screenClientId&&n.wsUrl&&B()}),e.onMounted(()=>{n.screenClientId&&n.wsUrl&&B()}),e.onUnmounted(()=>{i.value&&i.value.close(),a&&a.close()}),(l,s)=>e.renderSlot(l.$slots,"default")}}),G={install:d=>{d.component("VirtualHumanPersona",q),d.component("VirtualHumanEventAdapter",D)}};v.VirtualHumanEventAdapter=D,v.VirtualHumanPersona=q,v.default=G,Object.defineProperties(v,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "virtual-human-cf",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Vue3 Digital Human Component Package by cf ",
5
5
  "main": "dist/virtual-human-cf.umd.js",
6
6
  "module": "dist/virtual-human-cf.es.js",
@@ -18,7 +18,7 @@ const props = defineProps({
18
18
  }
19
19
  });
20
20
 
21
- const emit = defineEmits(['highlight', 'showDialog', 'end', 'pause', 'connected', 'error', 'play_complete']);
21
+ const emit = defineEmits(['eventNotifaction', 'end', 'pause', 'connected', 'error', 'playComplete']);
22
22
 
23
23
  const ws = ref<WebSocket | null>(null);
24
24
  const isConnected = ref(false);
@@ -28,6 +28,7 @@ let audioContext: AudioContext | null = null;
28
28
  let nextStartTime = 0;
29
29
  let isTtsComplete = false;
30
30
  let activeSources = 0;
31
+ let isIntentionallyPaused = false;
31
32
 
32
33
  const initAudioContext = () => {
33
34
  if (!audioContext) {
@@ -35,14 +36,14 @@ const initAudioContext = () => {
35
36
  sampleRate: 24000
36
37
  });
37
38
  }
38
- if (audioContext.state === 'suspended') {
39
+ if (audioContext.state === 'suspended' && !isIntentionallyPaused) {
39
40
  audioContext.resume();
40
41
  }
41
42
  };
42
43
 
43
44
  const checkPlayComplete = () => {
44
45
  if (isTtsComplete && activeSources === 0) {
45
- emit('play_complete', props.screenClientId);
46
+ emit('playComplete', props.screenClientId);
46
47
  isTtsComplete = false;
47
48
  }
48
49
  };
@@ -91,27 +92,30 @@ const handleAudioMessage = (base64Data: string) => {
91
92
  };
92
93
 
93
94
  const handleControlMessage = (action: string) => {
94
- if (!audioContext && action !== 'stop') return;
95
95
  switch (action) {
96
96
  case 'play':
97
97
  isTtsComplete = false;
98
98
  activeSources = 0;
99
99
  nextStartTime = 0;
100
+ isIntentionallyPaused = false;
100
101
  if (audioContext && audioContext.state === 'suspended') {
101
102
  audioContext.resume();
102
103
  }
103
104
  break;
104
105
  case 'resume':
106
+ isIntentionallyPaused = false;
105
107
  if (audioContext && audioContext.state === 'suspended') {
106
108
  audioContext.resume();
107
109
  }
108
110
  break;
109
111
  case 'pause':
112
+ isIntentionallyPaused = true;
110
113
  if (audioContext && audioContext.state === 'running') {
111
114
  audioContext.suspend();
112
115
  }
113
116
  break;
114
117
  case 'stop':
118
+ isIntentionallyPaused = false;
115
119
  if (audioContext) {
116
120
  audioContext.close();
117
121
  audioContext = null;
@@ -171,32 +175,27 @@ const connectWebSocket = () => {
171
175
 
172
176
  const handleMessage = (msg: any) => {
173
177
  const { type, payload, action } = msg;
174
- console.log("msgmsg",msg)
178
+ console.log("msgmsg-002",msg)
175
179
  switch (type) {
180
+ // 接收音频
176
181
  case 'audio':
177
182
  const base64Data = payload?.data || msg.data;
178
183
  if (base64Data) {
179
184
  handleAudioMessage(base64Data);
180
185
  }
181
186
  break;
182
- case 'dialog_event':
187
+ // 接收事件通知
188
+ case 'send_event':
183
189
  if (msg.event) {
184
- emit(msg.event as any, msg.params);
190
+ emit('eventNotifaction', msg);
185
191
  }
186
192
  break;
193
+ // 控制指令接口
187
194
  case 'control':
188
195
  if (action) {
189
196
  handleControlMessage(action);
190
197
  }
191
198
  break;
192
- case 'highlight':
193
- // 触发高亮大屏某区域事件
194
- emit('highlight', payload);
195
- break;
196
- case 'showDialog':
197
- // 触发弹窗显示事件
198
- emit('showDialog', payload);
199
- break;
200
199
  case 'end':
201
200
  // 触发数字人对话结束事件
202
201
  emit('end', payload);
@@ -19,6 +19,8 @@
19
19
  :muted="muted"
20
20
  playsinline
21
21
  loop
22
+ autoPlay
23
+ disablePictureInPicture="false"
22
24
  @play="handlePlay"
23
25
  @pause="handlePause"
24
26
  @ended="handleEnded"