virtual-human-cf 1.0.7 → 1.0.11

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
@@ -11,6 +11,11 @@
11
11
  - 暗黑模式支持
12
12
  - 响应式设计
13
13
 
14
+ ## 更新日志
15
+
16
+ ### 1.0.10
17
+ - 新增VirtualHumanPersona组件styles属性,用于控制视频容器 宽高 绝对定位位置等
18
+
14
19
  ## 安装
15
20
 
16
21
  ```bash
@@ -53,6 +58,7 @@ import { VirtualHumanPersona, VirtualHumanEventAdapter } from 'virtual-human-cf'
53
58
  | isDark | Boolean | false | 否 | 是否暗黑模式 |
54
59
  | screenClientId | String | - | 否 | 屏幕客户端 ID |
55
60
  | wsUrl | String | - | 否 | WebSocket 连接地址 |
61
+ | styles | Object | - | 否 | 组件样式,包含 width、height、left、bottom 等属性 |
56
62
 
57
63
  #### Events
58
64
 
@@ -73,6 +79,7 @@ import { VirtualHumanPersona, VirtualHumanEventAdapter } from 'virtual-human-cf'
73
79
  :is-dark="false"
74
80
  :screen-client-id="clientId"
75
81
  :ws-url="websocketUrl"
82
+ :styles="styles"
76
83
  @update:isPlaying="onPlayingChange"
77
84
  @ended="onEnded"
78
85
  />
@@ -86,7 +93,12 @@ const videoUrl = ref('https://example.com/video.mp4');
86
93
  const playing = ref(false);
87
94
  const clientId = ref('screen-123');
88
95
  const websocketUrl = ref('ws://localhost:8080/ws');
89
-
96
+ const styles = ref({
97
+ width:'400px',
98
+ height:'500px',
99
+ left: '16px',
100
+ bottom:'16px'
101
+ })
90
102
  const onPlayingChange = (isPlaying) => {
91
103
  console.log('播放状态改变:', isPlaying);
92
104
  };
@@ -119,8 +131,9 @@ const onEnded = () => {
119
131
  | error | 错误事件 | 错误信息 |
120
132
  | playComplete | 播放完成,数字人对话结束,关闭数字人 | - |
121
133
 
122
- ```json
123
- eventNotifaction => 载荷数据 :{
134
+ ```js
135
+ // eventNotifaction 载荷数据
136
+ {
124
137
 
125
138
  "type": "send_event",
126
139
  // 自定义事件名称
@@ -192,6 +205,7 @@ const onError = (error) => {
192
205
  :is-dark="isDarkMode"
193
206
  :screen-client-id="activeScreenClientId"
194
207
  :ws-url="websocketUrl"
208
+ :styles="styles"
195
209
  @update:isPlaying="onPersonaPlayingUpdate"
196
210
  @update:visible="onPersonaVisibleUpdate"
197
211
  @ended="onVideoEnded"
@@ -27,6 +27,15 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
27
27
  type: StringConstructor;
28
28
  required: false;
29
29
  };
30
+ styles: {
31
+ type: ObjectConstructor;
32
+ default: () => {
33
+ width: string;
34
+ height: string;
35
+ left: string;
36
+ bottom: string;
37
+ };
38
+ };
30
39
  }>, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
31
40
  ended: (...args: any[]) => void;
32
41
  "update:isPlaying": (...args: any[]) => void;
@@ -60,6 +69,15 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
60
69
  type: StringConstructor;
61
70
  required: false;
62
71
  };
72
+ styles: {
73
+ type: ObjectConstructor;
74
+ default: () => {
75
+ width: string;
76
+ height: string;
77
+ left: string;
78
+ bottom: string;
79
+ };
80
+ };
63
81
  }>> & Readonly<{
64
82
  onEnded?: ((...args: any[]) => any) | undefined;
65
83
  "onUpdate:isPlaying"?: ((...args: any[]) => any) | undefined;
@@ -69,5 +87,6 @@ declare const _default: import('vue').DefineComponent<import('vue').ExtractPropT
69
87
  isPlaying: boolean;
70
88
  muted: boolean;
71
89
  isDark: boolean;
90
+ styles: Record<string, any>;
72
91
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
73
92
  export default _default;
package/dist/style.css CHANGED
@@ -1 +1 @@
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
+ .fade-enter-active[data-v-7325596e],.fade-leave-active[data-v-7325596e]{transition:opacity .5s ease,transform .5s ease}.fade-enter-from[data-v-7325596e],.fade-leave-to[data-v-7325596e]{opacity:0;transform:translateY(10px)}.virtual-human-container[data-v-7325596e]{position:fixed;z-index:2147483647;overflow:visible}.video-wrapper[data-v-7325596e]{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-7325596e]:hover{border-color:#409eff}.virtual-human-container.is-dark .video-wrapper[data-v-7325596e]{background:#1f2937;box-shadow:0 10px 25px #00000080}.persona-video[data-v-7325596e]{width:100%;height:100%;object-fit:cover;transform-origin:center;transition:transform .1s ease-out}.resize-handle[data-v-7325596e]{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-7325596e]{opacity:1;pointer-events:auto}.resize-handle[data-v-7325596e]:hover{background:#000000b3;border-color:#fff}.resize-handle.top-left[data-v-7325596e]{top:10px;left:10px;cursor:nwse-resize}.resize-handle.top-right[data-v-7325596e]{top:10px;right:10px;cursor:nesw-resize}.resize-handle.bottom-left[data-v-7325596e]{bottom:10px;left:10px;cursor:nesw-resize}.resize-handle.bottom-right[data-v-7325596e]{bottom:10px;right:10px;cursor:nwse-resize}.overlay[data-v-7325596e]{position:absolute;top:1rem;right:1rem;z-index:10}.status-badge[data-v-7325596e]{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-7325596e]{background:#ef4444cc}.status-badge.playing[data-v-7325596e]{background:#22c55ecc}.dot[data-v-7325596e]{width:6px;height:6px;border-radius:50%;background-color:#fff}@keyframes pulse-7325596e{0%,to{opacity:1}50%{opacity:.5}}.animate-pulse[data-v-7325596e]{animation:pulse-7325596e 2s cubic-bezier(.4,0,.6,1) infinite}
@@ -1,11 +1,5 @@
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
- const ae = ["src", "muted"], se = { class: "overlay" }, le = {
3
- key: 0,
4
- class: "status-badge paused"
5
- }, oe = {
6
- key: 1,
7
- class: "status-badge playing"
8
- }, re = /* @__PURE__ */ j({
1
+ import { defineComponent as H, ref as g, watch as C, onMounted as E, onUnmounted as _, openBlock as A, createBlock as B, Transition as W, withCtx as $, createElementBlock as R, normalizeStyle as F, normalizeClass as N, createElementVNode as U, createCommentVNode as T, renderSlot as q } from "vue";
2
+ const M = ["src", "muted"], z = /* @__PURE__ */ H({
9
3
  __name: "VirtualHumanPersona",
10
4
  props: {
11
5
  // 视频源URL
@@ -43,141 +37,100 @@ const ae = ["src", "muted"], se = { class: "overlay" }, le = {
43
37
  wsUrl: {
44
38
  type: String,
45
39
  required: !1
40
+ },
41
+ styles: {
42
+ type: Object,
43
+ default: () => ({
44
+ width: "400px",
45
+ height: "500px",
46
+ left: "16px",
47
+ bottom: "16px"
48
+ })
46
49
  }
47
50
  },
48
51
  emits: ["update:isPlaying", "ended", "update:visible"],
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
- if (n.value && n.value.close(), !(!t.wsUrl || !t.screenClientId))
52
+ setup(r, { emit: b }) {
53
+ const e = r, n = b, a = g(null), h = g(null), t = g(null), d = g(!1), f = () => {
54
+ if (t.value && t.value.close(), !(!e.wsUrl || !e.screenClientId))
52
55
  try {
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
- }, n.value.onmessage = (e) => {
56
+ const i = new URL(e.wsUrl);
57
+ i.searchParams.append("sessionId", e.screenClientId + "-persona"), t.value = new WebSocket(i.toString()), t.value.onopen = () => {
58
+ d.value = !0, console.log(`[VirtualHumanPersona] Connected to ${e.wsUrl} for session ${e.screenClientId}-persona`);
59
+ }, t.value.onmessage = (u) => {
57
60
  try {
58
- const a = JSON.parse(e.data);
59
- C(a);
60
- } catch (a) {
61
- console.error("[VirtualHumanPersona] Failed to parse message:", e.data, a);
61
+ const c = JSON.parse(u.data);
62
+ m(c);
63
+ } catch (c) {
64
+ console.error("[VirtualHumanPersona] Failed to parse message:", u.data, c);
62
65
  }
63
- }, n.value.onerror = (e) => {
64
- console.error("[VirtualHumanPersona] WebSocket error:", e);
65
- }, n.value.onclose = () => {
66
- p.value = !1, console.log("[VirtualHumanPersona] WebSocket disconnected");
66
+ }, t.value.onerror = (u) => {
67
+ console.error("[VirtualHumanPersona] WebSocket error:", u);
68
+ }, t.value.onclose = () => {
69
+ d.value = !1, console.log("[VirtualHumanPersona] WebSocket disconnected");
67
70
  };
68
- } catch (o) {
69
- console.error("[VirtualHumanPersona] Failed to initialize WebSocket:", o);
71
+ } catch (i) {
72
+ console.error("[VirtualHumanPersona] Failed to initialize WebSocket:", i);
70
73
  }
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
+ }, m = (i) => {
75
+ const { type: u, action: c } = i;
76
+ u === "control" && (c === "play" || c === "resume" ? (n("update:isPlaying", !0), n("update:visible", !0)) : c === "pause" ? (n("update:isPlaying", !1), n("update:visible", !0)) : c === "stop" && (n("update:isPlaying", !1), n("update:visible", !1), a.value && (a.value.currentTime = 0)));
74
77
  };
75
- X(() => t.screenClientId, () => {
76
- t.screenClientId && t.wsUrl && b();
77
- }), X(() => t.wsUrl, () => {
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());
78
+ C(() => e.screenClientId, () => {
79
+ e.screenClientId && e.wsUrl && f();
80
+ }), C(() => e.wsUrl, () => {
81
+ e.screenClientId && e.wsUrl && f();
82
+ }), C(() => e.isPlaying, (i) => {
83
+ a.value && (i ? a.value.play().catch((u) => console.error("Video play failed:", u)) : a.value.pause());
81
84
  });
82
- const B = () => {
83
- t.isPlaying || i("update:isPlaying", !0);
84
- }, L = () => {
85
- t.isPlaying && i("update:isPlaying", !1);
86
- }, U = () => {
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);
85
+ const v = () => {
86
+ e.isPlaying || n("update:isPlaying", !0);
87
+ }, S = () => {
88
+ e.isPlaying && n("update:isPlaying", !1);
89
+ }, k = () => {
90
+ n("update:isPlaying", !1), n("ended");
100
91
  };
101
- return G(() => {
102
- t.isPlaying && r.value && r.value.play().catch((o) => console.error("Video play failed:", o)), t.screenClientId && t.wsUrl && b();
103
- }), K(() => {
104
- n.value && n.value.close();
105
- }), (o, e) => (S(), Q(Z, { name: "fade" }, {
106
- default: _(() => [
107
- c.visible ? (S(), E("div", {
92
+ return E(() => {
93
+ e.isPlaying && a.value && a.value.play().catch((i) => console.error("Video play failed:", i)), e.screenClientId && e.wsUrl && f();
94
+ }), _(() => {
95
+ t.value && t.value.close();
96
+ }), (i, u) => (A(), B(W, { name: "fade" }, {
97
+ default: $(() => [
98
+ r.visible ? (A(), R("div", {
108
99
  key: 0,
109
- class: te(["virtual-human-container", { "is-dark": c.isDark }]),
110
- style: ee({
111
- width: y.value + "px",
112
- height: g.value + "px",
113
- left: w.value + "px",
114
- bottom: z.value + "px"
115
- })
100
+ class: N(["virtual-human-container", { "is-dark": r.isDark }]),
101
+ style: F(r.styles)
116
102
  }, [
117
- R("div", {
103
+ U("div", {
118
104
  class: "video-wrapper",
119
105
  ref_key: "wrapperRef",
120
- ref: Y
106
+ ref: h
121
107
  }, [
122
- R("video", {
108
+ U("video", {
123
109
  ref_key: "videoRef",
124
- ref: r,
125
- src: c.videoSrc,
110
+ ref: a,
111
+ src: r.videoSrc,
126
112
  class: "persona-video",
127
- muted: c.muted,
113
+ muted: r.muted,
128
114
  playsinline: "",
129
115
  loop: "",
130
116
  autoPlay: "",
131
- disablePictureInPicture: "false",
132
- onPlay: B,
133
- onPause: L,
134
- onEnded: U
135
- }, null, 40, ae),
136
- c.visible ? (S(), E("div", {
137
- key: 0,
138
- class: "resize-handle top-left",
139
- onMousedown: e[0] || (e[0] = (a) => d("top-left", a)),
140
- onTouchstart: e[1] || (e[1] = (a) => d("top-left", a))
141
- }, null, 32)) : T("", !0),
142
- c.visible ? (S(), E("div", {
143
- key: 1,
144
- class: "resize-handle top-right",
145
- onMousedown: e[2] || (e[2] = (a) => d("top-right", a)),
146
- onTouchstart: e[3] || (e[3] = (a) => d("top-right", a))
147
- }, null, 32)) : T("", !0),
148
- c.visible ? (S(), E("div", {
149
- key: 2,
150
- class: "resize-handle bottom-left",
151
- onMousedown: e[4] || (e[4] = (a) => d("bottom-left", a)),
152
- onTouchstart: e[5] || (e[5] = (a) => d("bottom-left", a))
153
- }, null, 32)) : T("", !0),
154
- c.visible ? (S(), E("div", {
155
- key: 3,
156
- class: "resize-handle bottom-right",
157
- onMousedown: e[6] || (e[6] = (a) => d("bottom-right", a)),
158
- onTouchstart: e[7] || (e[7] = (a) => d("bottom-right", a))
159
- }, null, 32)) : T("", !0),
160
- R("div", se, [
161
- c.isPlaying ? (S(), E("div", oe, [...e[9] || (e[9] = [
162
- R("span", { class: "dot animate-pulse" }, null, -1),
163
- J(" 播放中 ", -1)
164
- ])])) : (S(), E("div", le, [...e[8] || (e[8] = [
165
- R("span", { class: "dot" }, null, -1),
166
- J(" 暂停中 ", -1)
167
- ])]))
168
- ])
117
+ disablePictureInPicture: !1,
118
+ onPlay: v,
119
+ onPause: S,
120
+ onEnded: k
121
+ }, null, 40, M)
169
122
  ], 512)
170
123
  ], 6)) : T("", !0)
171
124
  ]),
172
125
  _: 1
173
126
  }));
174
127
  }
175
- }), ie = (c, W) => {
176
- const t = c.__vccOpts || c;
177
- for (const [i, r] of W)
178
- t[i] = r;
179
- return t;
180
- }, ue = /* @__PURE__ */ ie(re, [["__scopeId", "data-v-6c9e435e"]]), ce = /* @__PURE__ */ j({
128
+ }), D = (r, b) => {
129
+ const e = r.__vccOpts || r;
130
+ for (const [n, a] of b)
131
+ e[n] = a;
132
+ return e;
133
+ }, O = /* @__PURE__ */ D(z, [["__scopeId", "data-v-7325596e"]]), J = /* @__PURE__ */ H({
181
134
  __name: "VirtualHumanEventAdapter",
182
135
  props: {
183
136
  // 屏幕客户端ID
@@ -192,114 +145,114 @@ const ae = ["src", "muted"], se = { class: "overlay" }, le = {
192
145
  }
193
146
  },
194
147
  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 = () => {
199
- n || (n = new (window.AudioContext || window.webkitAudioContext)({
148
+ setup(r, { emit: b }) {
149
+ const e = r, n = b, a = g(null), h = g(!1);
150
+ let t = null, d = 0, f = !1, m = 0, v = !1;
151
+ const S = () => {
152
+ t || (t = new (window.AudioContext || window.webkitAudioContext)({
200
153
  sampleRate: 24e3
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)
154
+ })), t.state === "suspended" && !v && t.resume();
155
+ }, k = () => {
156
+ f && m === 0 && (n("playComplete", e.screenClientId), f = !1);
157
+ }, i = (s) => {
158
+ if (S(), !!t)
206
159
  try {
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;
213
- const L = n.createBuffer(1, B.length, 24e3);
214
- L.getChannelData(0).set(B);
215
- const U = n.createBufferSource();
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();
160
+ const o = window.atob(s), l = o.length, y = new Uint8Array(l);
161
+ for (let p = 0; p < l; p++)
162
+ y[p] = o.charCodeAt(p);
163
+ const w = new Int16Array(y.buffer), V = new Float32Array(w.length);
164
+ for (let p = 0; p < w.length; p++)
165
+ V[p] = w[p] / 32768;
166
+ const I = t.createBuffer(1, V.length, 24e3);
167
+ I.getChannelData(0).set(V);
168
+ const P = t.createBufferSource();
169
+ P.buffer = I, P.connect(t.destination), d < t.currentTime && (d = t.currentTime), P.start(d), d += I.duration, m++, P.onended = () => {
170
+ m--, k();
218
171
  };
219
- } catch (l) {
220
- console.error("[VirtualHumanEventAdapter] Failed to decode and play audio:", l);
172
+ } catch (o) {
173
+ console.error("[VirtualHumanEventAdapter] Failed to decode and play audio:", o);
221
174
  }
222
- }, M = (s) => {
175
+ }, u = (s) => {
223
176
  switch (s) {
224
177
  case "play":
225
- y = !1, g = 0, p = 0, w = !1, n && n.state === "suspended" && n.resume();
178
+ f = !1, m = 0, d = 0, v = !1, t && t.state === "suspended" && t.resume();
226
179
  break;
227
180
  case "resume":
228
- w = !1, n && n.state === "suspended" && n.resume();
181
+ v = !1, t && t.state === "suspended" && t.resume();
229
182
  break;
230
183
  case "pause":
231
- w = !0, n && n.state === "running" && n.suspend();
184
+ v = !0, t && t.state === "running" && t.suspend();
232
185
  break;
233
186
  case "stop":
234
- w = !1, n && (n.close(), n = null), p = 0, y = !1, g = 0;
187
+ v = !1, t && (t.close(), t = null), d = 0, f = !1, m = 0;
235
188
  break;
236
189
  case "tts_complete":
237
- y = !0, I();
190
+ f = !0, k();
238
191
  break;
239
192
  default:
240
193
  console.warn(`[VirtualHumanEventAdapter] Unknown control action: ${s}`);
241
194
  }
242
- }, A = () => {
243
- r.value && r.value.close();
195
+ }, c = () => {
196
+ a.value && a.value.close();
244
197
  try {
245
- const s = new URL(t.wsUrl);
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) => {
198
+ const s = new URL(e.wsUrl);
199
+ s.searchParams.append("sessionId", e.screenClientId + "-event"), a.value = new WebSocket(s.toString()), a.value.onopen = () => {
200
+ h.value = !0, n("connected"), console.log(`[VirtualHumanEventAdapter] Connected to ${e.wsUrl} for session ${e.screenClientId}-event`);
201
+ }, a.value.onmessage = (o) => {
249
202
  try {
250
- const u = JSON.parse(l.data);
251
- k(u);
252
- } catch (u) {
253
- console.error("[VirtualHumanEventAdapter] Failed to parse message:", l.data, u);
203
+ const l = JSON.parse(o.data);
204
+ x(l);
205
+ } catch (l) {
206
+ console.error("[VirtualHumanEventAdapter] Failed to parse message:", o.data, l);
254
207
  }
255
- }, r.value.onerror = (l) => {
256
- console.error("[VirtualHumanEventAdapter] WebSocket error:", l), i("error", l);
257
- }, r.value.onclose = () => {
258
- Y.value = !1, console.log("[VirtualHumanEventAdapter] WebSocket disconnected");
208
+ }, a.value.onerror = (o) => {
209
+ console.error("[VirtualHumanEventAdapter] WebSocket error:", o), n("error", o);
210
+ }, a.value.onclose = () => {
211
+ h.value = !1, console.log("[VirtualHumanEventAdapter] WebSocket disconnected");
259
212
  };
260
213
  } catch (s) {
261
- console.error("[VirtualHumanEventAdapter] Failed to initialize WebSocket:", s), i("error", s);
214
+ console.error("[VirtualHumanEventAdapter] Failed to initialize WebSocket:", s), n("error", s);
262
215
  }
263
- }, k = (s) => {
264
- const { type: l, payload: u, action: b } = s;
265
- switch (console.log("msgmsg-002", s), l) {
216
+ }, x = (s) => {
217
+ const { type: o, payload: l, action: y } = s;
218
+ switch (o) {
266
219
  case "audio":
267
- const C = (u == null ? void 0 : u.data) || s.data;
268
- C && h(C);
220
+ const w = (l == null ? void 0 : l.data) || s.data;
221
+ w && i(w);
269
222
  break;
270
223
  case "send_event":
271
- s.event && i("eventNotifaction", s);
224
+ s.event && (console.log("adapter send_event:", s), n("eventNotifaction", s));
272
225
  break;
273
226
  case "control":
274
- b && M(b);
227
+ y && (console.log("adapter control:", y), u(y));
275
228
  break;
276
229
  case "end":
277
- i("end", u);
230
+ console.log("adapter end:", l), n("end", l);
278
231
  break;
279
232
  case "pause":
280
- i("pause", u);
233
+ console.log("adapter pause:", l), n("pause", l);
281
234
  break;
282
235
  default:
283
- console.warn(`[VirtualHumanEventAdapter] Unknown message type: ${l}`);
236
+ console.warn(`[VirtualHumanEventAdapter] Unknown message type: ${o}`);
284
237
  }
285
238
  };
286
- return X(() => t.screenClientId, () => {
287
- t.screenClientId && t.wsUrl && A();
288
- }), X(() => t.wsUrl, () => {
289
- t.screenClientId && t.wsUrl && A();
290
- }), G(() => {
291
- t.screenClientId && t.wsUrl && A();
292
- }), K(() => {
293
- r.value && r.value.close(), n && n.close();
294
- }), (s, l) => ne(s.$slots, "default");
239
+ return C(() => e.screenClientId, () => {
240
+ e.screenClientId && e.wsUrl && c();
241
+ }), C(() => e.wsUrl, () => {
242
+ e.screenClientId && e.wsUrl && c();
243
+ }), E(() => {
244
+ e.screenClientId && e.wsUrl && c();
245
+ }), _(() => {
246
+ a.value && a.value.close(), t && t.close();
247
+ }), (s, o) => q(s.$slots, "default");
295
248
  }
296
- }), de = (c) => {
297
- c.component("VirtualHumanPersona", ue), c.component("VirtualHumanEventAdapter", ce);
298
- }, fe = {
299
- install: de
249
+ }), L = (r) => {
250
+ r.component("VirtualHumanPersona", O), r.component("VirtualHumanEventAdapter", J);
251
+ }, G = {
252
+ install: L
300
253
  };
301
254
  export {
302
- ce as VirtualHumanEventAdapter,
303
- ue as VirtualHumanPersona,
304
- fe as default
255
+ J as VirtualHumanEventAdapter,
256
+ O as VirtualHumanPersona,
257
+ G as default
305
258
  };
@@ -1 +1 @@
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"}})});
1
+ (function(f,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],n):(f=typeof globalThis<"u"?globalThis:f||self,n(f.VirtualHumanCf={},f.Vue))})(this,function(f,n){"use strict";const H=["src","muted"],U=((r,b)=>{const e=r.__vccOpts||r;for(const[a,o]of b)e[a]=o;return e})(n.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},styles:{type:Object,default:()=>({width:"400px",height:"500px",left:"16px",bottom:"16px"})}},emits:["update:isPlaying","ended","update:visible"],setup(r,{emit:b}){const e=r,a=b,o=n.ref(null),C=n.ref(null),t=n.ref(null),p=n.ref(!1),m=()=>{if(t.value&&t.value.close(),!(!e.wsUrl||!e.screenClientId))try{const c=new URL(e.wsUrl);c.searchParams.append("sessionId",e.screenClientId+"-persona"),t.value=new WebSocket(c.toString()),t.value.onopen=()=>{p.value=!0,console.log(`[VirtualHumanPersona] Connected to ${e.wsUrl} for session ${e.screenClientId}-persona`)},t.value.onmessage=u=>{try{const d=JSON.parse(u.data);w(d)}catch(d){console.error("[VirtualHumanPersona] Failed to parse message:",u.data,d)}},t.value.onerror=u=>{console.error("[VirtualHumanPersona] WebSocket error:",u)},t.value.onclose=()=>{p.value=!1,console.log("[VirtualHumanPersona] WebSocket disconnected")}}catch(c){console.error("[VirtualHumanPersona] Failed to initialize WebSocket:",c)}},w=c=>{const{type:u,action:d}=c;u==="control"&&(d==="play"||d==="resume"?(a("update:isPlaying",!0),a("update:visible",!0)):d==="pause"?(a("update:isPlaying",!1),a("update:visible",!0)):d==="stop"&&(a("update:isPlaying",!1),a("update:visible",!1),o.value&&(o.value.currentTime=0)))};n.watch(()=>e.screenClientId,()=>{e.screenClientId&&e.wsUrl&&m()}),n.watch(()=>e.wsUrl,()=>{e.screenClientId&&e.wsUrl&&m()}),n.watch(()=>e.isPlaying,c=>{o.value&&(c?o.value.play().catch(u=>console.error("Video play failed:",u)):o.value.pause())});const g=()=>{e.isPlaying||a("update:isPlaying",!0)},S=()=>{e.isPlaying&&a("update:isPlaying",!1)},k=()=>{a("update:isPlaying",!1),a("ended")};return n.onMounted(()=>{e.isPlaying&&o.value&&o.value.play().catch(c=>console.error("Video play failed:",c)),e.screenClientId&&e.wsUrl&&m()}),n.onUnmounted(()=>{t.value&&t.value.close()}),(c,u)=>(n.openBlock(),n.createBlock(n.Transition,{name:"fade"},{default:n.withCtx(()=>[r.visible?(n.openBlock(),n.createElementBlock("div",{key:0,class:n.normalizeClass(["virtual-human-container",{"is-dark":r.isDark}]),style:n.normalizeStyle(r.styles)},[n.createElementVNode("div",{class:"video-wrapper",ref_key:"wrapperRef",ref:C},[n.createElementVNode("video",{ref_key:"videoRef",ref:o,src:r.videoSrc,class:"persona-video",muted:r.muted,playsinline:"",loop:"",autoPlay:"",disablePictureInPicture:!1,onPlay:g,onPause:S,onEnded:k},null,40,H)],512)],6)):n.createCommentVNode("",!0)]),_:1}))}}),[["__scopeId","data-v-7325596e"]]),A=n.defineComponent({__name:"VirtualHumanEventAdapter",props:{screenClientId:{type:String,required:!0},wsUrl:{type:String,required:!0}},emits:["eventNotifaction","end","pause","connected","error","playComplete"],setup(r,{emit:b}){const e=r,a=b,o=n.ref(null),C=n.ref(!1);let t=null,p=0,m=!1,w=0,g=!1;const S=()=>{t||(t=new(window.AudioContext||window.webkitAudioContext)({sampleRate:24e3})),t.state==="suspended"&&!g&&t.resume()},k=()=>{m&&w===0&&(a("playComplete",e.screenClientId),m=!1)},c=s=>{if(S(),!!t)try{const l=window.atob(s),i=l.length,h=new Uint8Array(i);for(let y=0;y<i;y++)h[y]=l.charCodeAt(y);const v=new Int16Array(h.buffer),V=new Float32Array(v.length);for(let y=0;y<v.length;y++)V[y]=v[y]/32768;const I=t.createBuffer(1,V.length,24e3);I.getChannelData(0).set(V);const P=t.createBufferSource();P.buffer=I,P.connect(t.destination),p<t.currentTime&&(p=t.currentTime),P.start(p),p+=I.duration,w++,P.onended=()=>{w--,k()}}catch(l){console.error("[VirtualHumanEventAdapter] Failed to decode and play audio:",l)}},u=s=>{switch(s){case"play":m=!1,w=0,p=0,g=!1,t&&t.state==="suspended"&&t.resume();break;case"resume":g=!1,t&&t.state==="suspended"&&t.resume();break;case"pause":g=!0,t&&t.state==="running"&&t.suspend();break;case"stop":g=!1,t&&(t.close(),t=null),p=0,m=!1,w=0;break;case"tts_complete":m=!0,k();break;default:console.warn(`[VirtualHumanEventAdapter] Unknown control action: ${s}`)}},d=()=>{o.value&&o.value.close();try{const s=new URL(e.wsUrl);s.searchParams.append("sessionId",e.screenClientId+"-event"),o.value=new WebSocket(s.toString()),o.value.onopen=()=>{C.value=!0,a("connected"),console.log(`[VirtualHumanEventAdapter] Connected to ${e.wsUrl} for session ${e.screenClientId}-event`)},o.value.onmessage=l=>{try{const i=JSON.parse(l.data);E(i)}catch(i){console.error("[VirtualHumanEventAdapter] Failed to parse message:",l.data,i)}},o.value.onerror=l=>{console.error("[VirtualHumanEventAdapter] WebSocket error:",l),a("error",l)},o.value.onclose=()=>{C.value=!1,console.log("[VirtualHumanEventAdapter] WebSocket disconnected")}}catch(s){console.error("[VirtualHumanEventAdapter] Failed to initialize WebSocket:",s),a("error",s)}},E=s=>{const{type:l,payload:i,action:h}=s;switch(l){case"audio":const v=(i==null?void 0:i.data)||s.data;v&&c(v);break;case"send_event":s.event&&(console.log("adapter send_event:",s),a("eventNotifaction",s));break;case"control":h&&(console.log("adapter control:",h),u(h));break;case"end":console.log("adapter end:",i),a("end",i);break;case"pause":console.log("adapter pause:",i),a("pause",i);break;default:console.warn(`[VirtualHumanEventAdapter] Unknown message type: ${l}`)}};return n.watch(()=>e.screenClientId,()=>{e.screenClientId&&e.wsUrl&&d()}),n.watch(()=>e.wsUrl,()=>{e.screenClientId&&e.wsUrl&&d()}),n.onMounted(()=>{e.screenClientId&&e.wsUrl&&d()}),n.onUnmounted(()=>{o.value&&o.value.close(),t&&t.close()}),(s,l)=>n.renderSlot(s.$slots,"default")}}),_={install:r=>{r.component("VirtualHumanPersona",U),r.component("VirtualHumanEventAdapter",A)}};f.VirtualHumanEventAdapter=A,f.VirtualHumanPersona=U,f.default=_,Object.defineProperties(f,{__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.7",
3
+ "version": "1.0.11",
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",
@@ -175,7 +175,7 @@ const connectWebSocket = () => {
175
175
 
176
176
  const handleMessage = (msg: any) => {
177
177
  const { type, payload, action } = msg;
178
- console.log("msgmsg-002",msg)
178
+
179
179
  switch (type) {
180
180
  // 接收音频
181
181
  case 'audio':
@@ -187,21 +187,25 @@ const handleMessage = (msg: any) => {
187
187
  // 接收事件通知
188
188
  case 'send_event':
189
189
  if (msg.event) {
190
+ console.log("adapter send_event:",msg)
190
191
  emit('eventNotifaction', msg);
191
192
  }
192
193
  break;
193
194
  // 控制指令接口
194
195
  case 'control':
195
196
  if (action) {
197
+ console.log("adapter control:",action)
196
198
  handleControlMessage(action);
197
199
  }
198
200
  break;
199
201
  case 'end':
200
202
  // 触发数字人对话结束事件
203
+ console.log("adapter end:",payload)
201
204
  emit('end', payload);
202
205
  break;
203
206
  case 'pause':
204
207
  // 触发暂停播放事件
208
+ console.log("adapter pause:",payload)
205
209
  emit('pause', payload);
206
210
  break;
207
211
  default:
@@ -4,12 +4,7 @@
4
4
  v-if="visible"
5
5
  class="virtual-human-container"
6
6
  :class="{ 'is-dark': isDark }"
7
- :style="{
8
- width: containerWidth + 'px',
9
- height: containerHeight + 'px',
10
- left: containerLeft + 'px',
11
- bottom: containerBottom + 'px'
12
- }"
7
+ :style="styles"
13
8
  >
14
9
  <div class="video-wrapper" ref="wrapperRef">
15
10
  <video
@@ -20,49 +15,11 @@
20
15
  playsinline
21
16
  loop
22
17
  autoPlay
23
- disablePictureInPicture="false"
18
+ :disablePictureInPicture="false"
24
19
  @play="handlePlay"
25
20
  @pause="handlePause"
26
21
  @ended="handleEnded"
27
22
  ></video>
28
-
29
- <!-- 缩放控制点 - 四个角 -->
30
- <div
31
- v-if="visible"
32
- class="resize-handle top-left"
33
- @mousedown="startResize('top-left', $event)"
34
- @touchstart="startResize('top-left', $event)"
35
- ></div>
36
- <div
37
- v-if="visible"
38
- class="resize-handle top-right"
39
- @mousedown="startResize('top-right', $event)"
40
- @touchstart="startResize('top-right', $event)"
41
- ></div>
42
- <div
43
- v-if="visible"
44
- class="resize-handle bottom-left"
45
- @mousedown="startResize('bottom-left', $event)"
46
- @touchstart="startResize('bottom-left', $event)"
47
- ></div>
48
- <div
49
- v-if="visible"
50
- class="resize-handle bottom-right"
51
- @mousedown="startResize('bottom-right', $event)"
52
- @touchstart="startResize('bottom-right', $event)"
53
- ></div>
54
-
55
- <!-- UI Overlay for controls or status -->
56
- <div class="overlay">
57
- <div v-if="!isPlaying" class="status-badge paused">
58
- <span class="dot"></span>
59
- 暂停中
60
- </div>
61
- <div v-else class="status-badge playing">
62
- <span class="dot animate-pulse"></span>
63
- 播放中
64
- </div>
65
- </div>
66
23
  </div>
67
24
  </div>
68
25
  </Transition>
@@ -106,6 +63,15 @@ const props = defineProps({
106
63
  wsUrl: {
107
64
  type: String,
108
65
  required: false,
66
+ },
67
+ styles: {
68
+ type: Object,
69
+ default: () => ({
70
+ width:'400px',
71
+ height:'500px',
72
+ left: '16px',
73
+ bottom:'16px'
74
+ })
109
75
  }
110
76
  });
111
77
 
@@ -117,20 +83,6 @@ const wrapperRef = ref<HTMLDivElement | null>(null);
117
83
  const ws = ref<WebSocket | null>(null);
118
84
  const isConnected = ref(false);
119
85
 
120
- // 缩放相关状态
121
- const containerWidth = ref(400);
122
- const containerHeight = ref(500);
123
- const containerLeft = ref(16);
124
- const containerBottom = ref(16);
125
-
126
- const isResizing = ref(false);
127
- const resizeHandle = ref<string | null>(null);
128
- const startX = ref(0);
129
- const startY = ref(0);
130
- const startW = ref(0);
131
- const startH = ref(0);
132
- const startL = ref(0);
133
- const startB = ref(0);
134
86
 
135
87
  const connectWebSocket = () => {
136
88
  if (ws.value) {
@@ -180,6 +132,7 @@ const handleMessage = (msg: any) => {
180
132
  } else if (action === 'pause') {
181
133
  emit('update:isPlaying', false);
182
134
  // 暂停时不隐藏视频
135
+ emit('update:visible', true);
183
136
  } else if (action === 'stop') {
184
137
  emit('update:isPlaying', false);
185
138
  emit('update:visible', false);
@@ -229,99 +182,7 @@ const handleEnded = () => {
229
182
  emit('ended');
230
183
  };
231
184
 
232
- // 缩放功能
233
- const startResize = (handle: string, event: MouseEvent | TouchEvent) => {
234
- event.preventDefault();
235
- isResizing.value = true;
236
- resizeHandle.value = handle;
237
-
238
- const clientX = 'clientX' in event ? event.clientX : event.touches[0].clientX;
239
- const clientY = 'clientY' in event ? event.clientY : event.touches[0].clientY;
240
- startX.value = clientX;
241
- startY.value = clientY;
242
-
243
- startW.value = containerWidth.value;
244
- startH.value = containerHeight.value;
245
- startL.value = containerLeft.value;
246
- startB.value = containerBottom.value;
247
-
248
- document.addEventListener('mousemove', handleResize);
249
- document.addEventListener('mouseup', stopResize);
250
- document.addEventListener('touchmove', handleResize, { passive: false });
251
- document.addEventListener('touchend', stopResize);
252
- };
253
-
254
- const handleResize = (event: MouseEvent | TouchEvent) => {
255
- if (!isResizing.value) return;
256
- event.preventDefault();
257
-
258
- const clientX = 'clientX' in event ? event.clientX : event.touches[0].clientX;
259
- const clientY = 'clientY' in event ? event.clientY : event.touches[0].clientY;
260
-
261
- const deltaX = clientX - startX.value;
262
- const deltaY = clientY - startY.value;
263
-
264
- const minWidth = 200;
265
- const minHeight = 250;
266
- const maxWidth = 800;
267
- const maxHeight = 1000;
268
-
269
- let newW = startW.value;
270
- let newH = startH.value;
271
- let newL = startL.value;
272
- let newB = startB.value;
273
-
274
- if (resizeHandle.value === 'bottom-right') {
275
- newW = startW.value + deltaX;
276
- newB = startB.value - deltaY;
277
- newH = startH.value + deltaY;
278
- } else if (resizeHandle.value === 'bottom-left') {
279
- newL = startL.value + deltaX;
280
- newW = startW.value - deltaX;
281
- newB = startB.value - deltaY;
282
- newH = startH.value + deltaY;
283
- } else if (resizeHandle.value === 'top-right') {
284
- newW = startW.value + deltaX;
285
- newH = startH.value - deltaY;
286
- } else if (resizeHandle.value === 'top-left') {
287
- newL = startL.value + deltaX;
288
- newW = startW.value - deltaX;
289
- newH = startH.value - deltaY;
290
- }
291
-
292
- // 约束最小宽高
293
- if (newW < minWidth) {
294
- if (newL !== startL.value) newL -= (minWidth - newW);
295
- newW = minWidth;
296
- }
297
- if (newH < minHeight) {
298
- if (newB !== startB.value) newB -= (minHeight - newH);
299
- newH = minHeight;
300
- }
301
- // 约束最大宽高
302
- if (newW > maxWidth) {
303
- if (newL !== startL.value) newL += (newW - maxWidth);
304
- newW = maxWidth;
305
- }
306
- if (newH > maxHeight) {
307
- if (newB !== startB.value) newB += (newH - maxHeight);
308
- newH = maxHeight;
309
- }
310
185
 
311
- containerWidth.value = newW;
312
- containerHeight.value = newH;
313
- containerLeft.value = newL;
314
- containerBottom.value = newB;
315
- };
316
-
317
- const stopResize = () => {
318
- isResizing.value = false;
319
- resizeHandle.value = null;
320
- document.removeEventListener('mousemove', handleResize);
321
- document.removeEventListener('mouseup', stopResize);
322
- document.removeEventListener('touchmove', handleResize);
323
- document.removeEventListener('touchend', stopResize);
324
- };
325
186
 
326
187
  onMounted(() => {
327
188
  if (props.isPlaying && videoRef.value) {