virtual-human-cf 1.0.2 → 1.0.4
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/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.fade-enter-active[data-v-
|
|
1
|
+
.fade-enter-active[data-v-19d39d2e],.fade-leave-active[data-v-19d39d2e]{transition:opacity .5s ease,transform .5s ease}.fade-enter-from[data-v-19d39d2e],.fade-leave-to[data-v-19d39d2e]{opacity:0;transform:translateY(10px)}.virtual-human-container[data-v-19d39d2e]{position:fixed;left:16px;bottom:16px;width:400px;height:500px;z-index:2147483647;overflow:visible}.video-wrapper[data-v-19d39d2e]{position:relative;width:400px;height:500px;border-radius:1rem;overflow:hidden;box-shadow:0 10px 25px #0000001a;background:#fff;transition:background-color .3s ease,box-shadow .3s ease}.virtual-human-container.is-dark .video-wrapper[data-v-19d39d2e]{background:#1f2937;box-shadow:0 10px 25px #00000080}.persona-video[data-v-19d39d2e]{width:100%;height:100%;object-fit:cover;transform-origin:center;transition:transform .1s ease-out}.resize-handle[data-v-19d39d2e]{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;transition:opacity .3s ease,background .2s,border-color .2s}.video-wrapper:hover .resize-handle[data-v-19d39d2e]{opacity:1}.resize-handle[data-v-19d39d2e]:hover{background:#000000b3;border-color:#fff}.resize-handle.top-left[data-v-19d39d2e]{top:10px;left:10px;cursor:nwse-resize}.resize-handle.top-right[data-v-19d39d2e]{top:10px;right:10px;cursor:nesw-resize}.resize-handle.bottom-left[data-v-19d39d2e]{bottom:10px;left:10px;cursor:nesw-resize}.resize-handle.bottom-right[data-v-19d39d2e]{bottom:10px;right:10px;cursor:nwse-resize}.overlay[data-v-19d39d2e]{position:absolute;top:1rem;right:1rem;z-index:10}.status-badge[data-v-19d39d2e]{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-19d39d2e]{background:#ef4444cc}.status-badge.playing[data-v-19d39d2e]{background:#22c55ecc}.dot[data-v-19d39d2e]{width:6px;height:6px;border-radius:50%;background-color:#fff}@keyframes pulse-19d39d2e{0%,to{opacity:1}50%{opacity:.5}}.animate-pulse[data-v-19d39d2e]{animation:pulse-19d39d2e 2s cubic-bezier(.4,0,.6,1) infinite}
|
|
@@ -70,7 +70,7 @@ const j = ["src", "muted"], G = { class: "overlay" }, K = {
|
|
|
70
70
|
}
|
|
71
71
|
}, c = (r) => {
|
|
72
72
|
const { type: e, action: a } = r;
|
|
73
|
-
e === "control" && (a === "play" ? (s("update:isPlaying", !0), s("update:visible", !0)) : a === "pause" ? s("update:isPlaying", !1) : a === "stop" && (s("update:isPlaying", !1), s("update:visible", !1), l.value && (l.value.currentTime = 0)));
|
|
73
|
+
e === "control" && (a === "play" || a === "resume" ? (s("update:isPlaying", !0), s("update:visible", !0)) : a === "pause" ? s("update:isPlaying", !1) : a === "stop" && (s("update:isPlaying", !1), s("update:visible", !1), l.value && (l.value.currentTime = 0)));
|
|
74
74
|
};
|
|
75
75
|
z(() => t.screenClientId, () => {
|
|
76
76
|
t.screenClientId && t.wsUrl && u();
|
|
@@ -111,7 +111,8 @@ const j = ["src", "muted"], G = { class: "overlay" }, K = {
|
|
|
111
111
|
H("div", {
|
|
112
112
|
class: "video-wrapper",
|
|
113
113
|
ref_key: "wrapperRef",
|
|
114
|
-
ref: k
|
|
114
|
+
ref: k,
|
|
115
|
+
style: O({ transform: `scale(${V.value})`, transformOrigin: "left bottom" })
|
|
115
116
|
}, [
|
|
116
117
|
H("video", {
|
|
117
118
|
ref_key: "videoRef",
|
|
@@ -123,9 +124,8 @@ const j = ["src", "muted"], G = { class: "overlay" }, K = {
|
|
|
123
124
|
loop: "",
|
|
124
125
|
onPlay: w,
|
|
125
126
|
onPause: g,
|
|
126
|
-
onEnded: A
|
|
127
|
-
|
|
128
|
-
}, null, 44, j),
|
|
127
|
+
onEnded: A
|
|
128
|
+
}, null, 40, j),
|
|
129
129
|
i.visible ? (h(), b("div", {
|
|
130
130
|
key: 0,
|
|
131
131
|
class: "resize-handle top-left",
|
|
@@ -159,7 +159,7 @@ const j = ["src", "muted"], G = { class: "overlay" }, K = {
|
|
|
159
159
|
L(" 暂停中 ", -1)
|
|
160
160
|
])]))
|
|
161
161
|
])
|
|
162
|
-
],
|
|
162
|
+
], 4)
|
|
163
163
|
], 2)) : I("", !0)
|
|
164
164
|
]),
|
|
165
165
|
_: 1
|
|
@@ -170,7 +170,7 @@ const j = ["src", "muted"], G = { class: "overlay" }, K = {
|
|
|
170
170
|
for (const [s, l] of P)
|
|
171
171
|
t[s] = l;
|
|
172
172
|
return t;
|
|
173
|
-
}, ee = /* @__PURE__ */ _(Z, [["__scopeId", "data-v-
|
|
173
|
+
}, ee = /* @__PURE__ */ _(Z, [["__scopeId", "data-v-19d39d2e"]]), te = /* @__PURE__ */ T({
|
|
174
174
|
__name: "VirtualHumanEventAdapter",
|
|
175
175
|
props: {
|
|
176
176
|
// 屏幕客户端ID
|
|
@@ -212,6 +212,7 @@ const j = ["src", "muted"], G = { class: "overlay" }, K = {
|
|
|
212
212
|
if (n)
|
|
213
213
|
switch (o) {
|
|
214
214
|
case "play":
|
|
215
|
+
case "resume":
|
|
215
216
|
n.state === "suspended" && n.resume();
|
|
216
217
|
break;
|
|
217
218
|
case "pause":
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(p,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(p=typeof globalThis<"u"?globalThis:p||self,e(p.VirtualHumanCf={},p.Vue))})(this,function(p,e){"use strict";const z=["src","muted"],N={class:"overlay"},T={key:0,class:"status-badge paused"},R={key:1,class:"status-badge playing"},I=((c,V)=>{const n=c.__vccOpts||c;for(const[s,r]of V)n[s]=r;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(c,{emit:V}){const n=c,s=V,r=e.ref(null),v=e.ref(null),o=e.ref(null),g=e.ref(!1),E=e.ref(1),S=e.ref(!1),h=e.ref(null),b=e.ref(0),H=e.ref(0),l=e.ref(1),d=()=>{if(o.value&&o.value.close(),!(!n.wsUrl||!n.screenClientId))try{const i=new URL(n.wsUrl);i.searchParams.append("sessionId",n.screenClientId+"-persona"),o.value=new WebSocket(i.toString()),o.value.onopen=()=>{g.value=!0,console.log(`[VirtualHumanPersona] Connected to ${n.wsUrl} for session ${n.screenClientId}-persona`)},o.value.onmessage=t=>{try{const a=JSON.parse(t.data);u(a)}catch(a){console.error("[VirtualHumanPersona] Failed to parse message:",t.data,a)}},o.value.onerror=t=>{console.error("[VirtualHumanPersona] WebSocket error:",t)},o.value.onclose=()=>{g.value=!1,console.log("[VirtualHumanPersona] WebSocket disconnected")}}catch(i){console.error("[VirtualHumanPersona] Failed to initialize WebSocket:",i)}},u=i=>{const{type:t,action:a}=i;t==="control"&&(a==="play"?(s("update:isPlaying",!0),s("update:visible",!0)):a==="pause"?s("update:isPlaying",!1):a==="stop"&&(s("update:isPlaying",!1),s("update:visible",!1),r.value&&(r.value.currentTime=0)))};e.watch(()=>n.screenClientId,()=>{n.screenClientId&&n.wsUrl&&d()}),e.watch(()=>n.wsUrl,()=>{n.screenClientId&&n.wsUrl&&d()}),e.watch(()=>n.isPlaying,i=>{r.value&&(i?r.value.play().catch(t=>console.error("Video play failed:",t)):r.value.pause())});const k=()=>{n.isPlaying||s("update:isPlaying",!0)},y=()=>{n.isPlaying&&s("update:isPlaying",!1)},P=()=>{s("update:isPlaying",!1),s("ended")},m=(i,t)=>{S.value=!0,h.value=i,l.value=E.value;const a="clientX"in t?t.clientX:t.touches[0].clientX,C="clientY"in t?t.clientY:t.touches[0].clientY;b.value=a,H.value=C,document.addEventListener("mousemove",w),document.addEventListener("mouseup",f),document.addEventListener("touchmove",w),document.addEventListener("touchend",f)},w=i=>{if(!S.value||!v.value)return;const t="clientX"in i?i.clientX:i.touches[0].clientX,a="clientY"in i?i.clientY:i.touches[0].clientY,C=t-b.value,U=a-H.value,x=v.value.getBoundingClientRect(),W=.5,X=2;let B=l.value;const A=Math.min(x.width,x.height);h.value==="bottom-right"?B=l.value+(C+U)/A:h.value==="bottom-left"?B=l.value+(-C+U)/A:h.value==="top-right"?B=l.value+(C-U)/A:h.value==="top-left"&&(B=l.value+(-C-U)/A),E.value=Math.max(W,Math.min(X,B))},f=()=>{S.value=!1,h.value=null,document.removeEventListener("mousemove",w),document.removeEventListener("mouseup",f),document.removeEventListener("touchmove",w),document.removeEventListener("touchend",f)};return e.onMounted(()=>{n.isPlaying&&r.value&&r.value.play().catch(i=>console.error("Video play failed:",i)),n.screenClientId&&n.wsUrl&&d()}),e.onUnmounted(()=>{o.value&&o.value.close()}),(i,t)=>(e.openBlock(),e.createBlock(e.Transition,{name:"fade"},{default:e.withCtx(()=>[c.visible?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(["virtual-human-container",{"is-dark":c.isDark}])},[e.createElementVNode("div",{class:"video-wrapper",ref_key:"wrapperRef",ref:v},[e.createElementVNode("video",{ref_key:"videoRef",ref:r,src:c.videoSrc,class:"persona-video",muted:c.muted,playsinline:"",loop:"",onPlay:k,onPause:y,onEnded:P
|
|
1
|
+
(function(p,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(p=typeof globalThis<"u"?globalThis:p||self,e(p.VirtualHumanCf={},p.Vue))})(this,function(p,e){"use strict";const z=["src","muted"],N={class:"overlay"},T={key:0,class:"status-badge paused"},R={key:1,class:"status-badge playing"},I=((c,V)=>{const n=c.__vccOpts||c;for(const[s,r]of V)n[s]=r;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(c,{emit:V}){const n=c,s=V,r=e.ref(null),v=e.ref(null),o=e.ref(null),g=e.ref(!1),E=e.ref(1),S=e.ref(!1),h=e.ref(null),b=e.ref(0),H=e.ref(0),l=e.ref(1),d=()=>{if(o.value&&o.value.close(),!(!n.wsUrl||!n.screenClientId))try{const i=new URL(n.wsUrl);i.searchParams.append("sessionId",n.screenClientId+"-persona"),o.value=new WebSocket(i.toString()),o.value.onopen=()=>{g.value=!0,console.log(`[VirtualHumanPersona] Connected to ${n.wsUrl} for session ${n.screenClientId}-persona`)},o.value.onmessage=t=>{try{const a=JSON.parse(t.data);u(a)}catch(a){console.error("[VirtualHumanPersona] Failed to parse message:",t.data,a)}},o.value.onerror=t=>{console.error("[VirtualHumanPersona] WebSocket error:",t)},o.value.onclose=()=>{g.value=!1,console.log("[VirtualHumanPersona] WebSocket disconnected")}}catch(i){console.error("[VirtualHumanPersona] Failed to initialize WebSocket:",i)}},u=i=>{const{type:t,action:a}=i;t==="control"&&(a==="play"||a==="resume"?(s("update:isPlaying",!0),s("update:visible",!0)):a==="pause"?s("update:isPlaying",!1):a==="stop"&&(s("update:isPlaying",!1),s("update:visible",!1),r.value&&(r.value.currentTime=0)))};e.watch(()=>n.screenClientId,()=>{n.screenClientId&&n.wsUrl&&d()}),e.watch(()=>n.wsUrl,()=>{n.screenClientId&&n.wsUrl&&d()}),e.watch(()=>n.isPlaying,i=>{r.value&&(i?r.value.play().catch(t=>console.error("Video play failed:",t)):r.value.pause())});const k=()=>{n.isPlaying||s("update:isPlaying",!0)},y=()=>{n.isPlaying&&s("update:isPlaying",!1)},P=()=>{s("update:isPlaying",!1),s("ended")},m=(i,t)=>{S.value=!0,h.value=i,l.value=E.value;const a="clientX"in t?t.clientX:t.touches[0].clientX,C="clientY"in t?t.clientY:t.touches[0].clientY;b.value=a,H.value=C,document.addEventListener("mousemove",w),document.addEventListener("mouseup",f),document.addEventListener("touchmove",w),document.addEventListener("touchend",f)},w=i=>{if(!S.value||!v.value)return;const t="clientX"in i?i.clientX:i.touches[0].clientX,a="clientY"in i?i.clientY:i.touches[0].clientY,C=t-b.value,U=a-H.value,x=v.value.getBoundingClientRect(),W=.5,X=2;let B=l.value;const A=Math.min(x.width,x.height);h.value==="bottom-right"?B=l.value+(C+U)/A:h.value==="bottom-left"?B=l.value+(-C+U)/A:h.value==="top-right"?B=l.value+(C-U)/A:h.value==="top-left"&&(B=l.value+(-C-U)/A),E.value=Math.max(W,Math.min(X,B))},f=()=>{S.value=!1,h.value=null,document.removeEventListener("mousemove",w),document.removeEventListener("mouseup",f),document.removeEventListener("touchmove",w),document.removeEventListener("touchend",f)};return e.onMounted(()=>{n.isPlaying&&r.value&&r.value.play().catch(i=>console.error("Video play failed:",i)),n.screenClientId&&n.wsUrl&&d()}),e.onUnmounted(()=>{o.value&&o.value.close()}),(i,t)=>(e.openBlock(),e.createBlock(e.Transition,{name:"fade"},{default:e.withCtx(()=>[c.visible?(e.openBlock(),e.createElementBlock("div",{key:0,class:e.normalizeClass(["virtual-human-container",{"is-dark":c.isDark}])},[e.createElementVNode("div",{class:"video-wrapper",ref_key:"wrapperRef",ref:v,style:e.normalizeStyle({transform:`scale(${E.value})`,transformOrigin:"left bottom"})},[e.createElementVNode("video",{ref_key:"videoRef",ref:r,src:c.videoSrc,class:"persona-video",muted:c.muted,playsinline:"",loop:"",onPlay:k,onPause:y,onEnded:P},null,40,z),c.visible?(e.openBlock(),e.createElementBlock("div",{key:0,class:"resize-handle top-left",onMousedown:t[0]||(t[0]=a=>m("top-left",a)),onTouchstart:t[1]||(t[1]=a=>m("top-left",a))},null,32)):e.createCommentVNode("",!0),c.visible?(e.openBlock(),e.createElementBlock("div",{key:1,class:"resize-handle top-right",onMousedown:t[2]||(t[2]=a=>m("top-right",a)),onTouchstart:t[3]||(t[3]=a=>m("top-right",a))},null,32)):e.createCommentVNode("",!0),c.visible?(e.openBlock(),e.createElementBlock("div",{key:2,class:"resize-handle bottom-left",onMousedown:t[4]||(t[4]=a=>m("bottom-left",a)),onTouchstart:t[5]||(t[5]=a=>m("bottom-left",a))},null,32)):e.createCommentVNode("",!0),c.visible?(e.openBlock(),e.createElementBlock("div",{key:3,class:"resize-handle bottom-right",onMousedown:t[6]||(t[6]=a=>m("bottom-right",a)),onTouchstart:t[7]||(t[7]=a=>m("bottom-right",a))},null,32)):e.createCommentVNode("",!0),e.createElementVNode("div",N,[c.isPlaying?(e.openBlock(),e.createElementBlock("div",R,[...t[9]||(t[9]=[e.createElementVNode("span",{class:"dot animate-pulse"},null,-1),e.createTextVNode(" 播放中 ",-1)])])):(e.openBlock(),e.createElementBlock("div",T,[...t[8]||(t[8]=[e.createElementVNode("span",{class:"dot"},null,-1),e.createTextVNode(" 暂停中 ",-1)])]))])],4)],2)):e.createCommentVNode("",!0)]),_:1}))}}),[["__scopeId","data-v-19d39d2e"]]),M=e.defineComponent({__name:"VirtualHumanEventAdapter",props:{screenClientId:{type:String,required:!0},wsUrl:{type:String,required:!0}},emits:["highlight","showDialog","end","pause","connected","error"],setup(c,{emit:V}){const n=c,s=V,r=e.ref(null),v=e.ref(!1);let o=null,g=0;const E=()=>{o||(o=new(window.AudioContext||window.webkitAudioContext)({sampleRate:24e3})),o.state==="suspended"&&o.resume()},S=l=>{if(E(),!!o)try{const d=window.atob(l),u=d.length,k=new Uint8Array(u);for(let f=0;f<u;f++)k[f]=d.charCodeAt(f);const y=new Int16Array(k.buffer),P=new Float32Array(y.length);for(let f=0;f<y.length;f++)P[f]=y[f]/32768;const m=o.createBuffer(1,P.length,24e3);m.getChannelData(0).set(P);const w=o.createBufferSource();w.buffer=m,w.connect(o.destination),g<o.currentTime&&(g=o.currentTime),w.start(g),g+=m.duration}catch(d){console.error("[VirtualHumanEventAdapter] Failed to decode and play audio:",d)}},h=l=>{if(o)switch(l){case"play":case"resume":o.state==="suspended"&&o.resume();break;case"pause":o.state==="running"&&o.suspend();break;case"stop":o.close(),o=null,g=0;break;default:console.warn(`[VirtualHumanEventAdapter] Unknown control action: ${l}`)}},b=()=>{r.value&&r.value.close();try{const l=new URL(n.wsUrl);l.searchParams.append("sessionId",n.screenClientId+"-event"),r.value=new WebSocket(l.toString()),r.value.onopen=()=>{v.value=!0,s("connected"),console.log(`[VirtualHumanEventAdapter] Connected to ${n.wsUrl} for session ${n.screenClientId}-event`)},r.value.onmessage=d=>{try{const u=JSON.parse(d.data);H(u)}catch(u){console.error("[VirtualHumanEventAdapter] Failed to parse message:",d.data,u)}},r.value.onerror=d=>{console.error("[VirtualHumanEventAdapter] WebSocket error:",d),s("error",d)},r.value.onclose=()=>{v.value=!1,console.log("[VirtualHumanEventAdapter] WebSocket disconnected")}}catch(l){console.error("[VirtualHumanEventAdapter] Failed to initialize WebSocket:",l),s("error",l)}},H=l=>{const{type:d,payload:u,action:k}=l;switch(console.log("msgmsg",l),d){case"audio":const y=(u==null?void 0:u.data)||l.data;y&&S(y);break;case"dialog_event":l.event&&s(l.event,l.params);break;case"control":k&&h(k);break;case"highlight":s("highlight",u);break;case"showDialog":s("showDialog",u);break;case"end":s("end",u);break;case"pause":s("pause",u);break;default:console.warn(`[VirtualHumanEventAdapter] Unknown message type: ${d}`)}};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(()=>{r.value&&r.value.close(),o&&o.close()}),(l,d)=>e.renderSlot(l.$slots,"default")}}),L={install:c=>{c.component("VirtualHumanPersona",I),c.component("VirtualHumanEventAdapter",M)}};p.VirtualHumanEventAdapter=M,p.VirtualHumanPersona=I,p.default=L,Object.defineProperties(p,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
package/package.json
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
class="virtual-human-container"
|
|
6
6
|
:class="{ 'is-dark': isDark }"
|
|
7
7
|
>
|
|
8
|
-
<div
|
|
8
|
+
<div
|
|
9
|
+
class="video-wrapper"
|
|
10
|
+
ref="wrapperRef"
|
|
11
|
+
:style="{ transform: `scale(${scale})`, transformOrigin: 'left bottom' }"
|
|
12
|
+
>
|
|
9
13
|
<video
|
|
10
14
|
ref="videoRef"
|
|
11
15
|
:src="videoSrc"
|
|
@@ -16,7 +20,6 @@
|
|
|
16
20
|
@play="handlePlay"
|
|
17
21
|
@pause="handlePause"
|
|
18
22
|
@ended="handleEnded"
|
|
19
|
-
:style="{ transform: `scale(${scale})` }"
|
|
20
23
|
></video>
|
|
21
24
|
|
|
22
25
|
<!-- 缩放控制点 - 四个角 -->
|
|
@@ -160,7 +163,7 @@ const connectWebSocket = () => {
|
|
|
160
163
|
const handleMessage = (msg: any) => {
|
|
161
164
|
const { type, action } = msg;
|
|
162
165
|
if (type === 'control') {
|
|
163
|
-
if (action === 'play') {
|
|
166
|
+
if (action === 'play' || action === 'resume') {
|
|
164
167
|
emit('update:isPlaying', true);
|
|
165
168
|
emit('update:visible', true);
|
|
166
169
|
} else if (action === 'pause') {
|
|
@@ -300,29 +303,31 @@ onUnmounted(() => {
|
|
|
300
303
|
}
|
|
301
304
|
|
|
302
305
|
.virtual-human-container {
|
|
306
|
+
position: fixed;
|
|
307
|
+
left: 16px;
|
|
308
|
+
bottom: 16px;
|
|
309
|
+
width: 400px;
|
|
310
|
+
height: 500px;
|
|
311
|
+
z-index: 2147483647;
|
|
312
|
+
overflow: visible;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.video-wrapper {
|
|
303
316
|
position: relative;
|
|
304
|
-
width:
|
|
305
|
-
|
|
317
|
+
width: 400px;
|
|
318
|
+
height: 500px;
|
|
306
319
|
border-radius: 1rem;
|
|
307
320
|
overflow: hidden;
|
|
308
321
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
|
|
309
322
|
background: #ffffff;
|
|
310
|
-
transition: background-color 0.3s ease;
|
|
323
|
+
transition: background-color 0.3s ease, box-shadow 0.3s ease;
|
|
311
324
|
}
|
|
312
325
|
|
|
313
|
-
.virtual-human-container.is-dark {
|
|
326
|
+
.virtual-human-container.is-dark .video-wrapper {
|
|
314
327
|
background: #1f2937;
|
|
315
328
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
|
|
316
329
|
}
|
|
317
330
|
|
|
318
|
-
.video-wrapper {
|
|
319
|
-
position: relative;
|
|
320
|
-
width: 100%;
|
|
321
|
-
aspect-ratio: 9 / 16; /* Portrait ratio for digital human */
|
|
322
|
-
background: #000;
|
|
323
|
-
overflow: visible;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
331
|
.persona-video {
|
|
327
332
|
width: 100%;
|
|
328
333
|
height: 100%;
|
|
@@ -340,7 +345,12 @@ onUnmounted(() => {
|
|
|
340
345
|
border-radius: 4px;
|
|
341
346
|
cursor: pointer;
|
|
342
347
|
z-index: 20;
|
|
343
|
-
|
|
348
|
+
opacity: 0;
|
|
349
|
+
transition: opacity 0.3s ease, background 0.2s, border-color 0.2s;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.video-wrapper:hover .resize-handle {
|
|
353
|
+
opacity: 1;
|
|
344
354
|
}
|
|
345
355
|
|
|
346
356
|
.resize-handle:hover {
|
|
@@ -349,26 +359,26 @@ onUnmounted(() => {
|
|
|
349
359
|
}
|
|
350
360
|
|
|
351
361
|
.resize-handle.top-left {
|
|
352
|
-
top:
|
|
353
|
-
left:
|
|
362
|
+
top: 10px;
|
|
363
|
+
left: 10px;
|
|
354
364
|
cursor: nwse-resize;
|
|
355
365
|
}
|
|
356
366
|
|
|
357
367
|
.resize-handle.top-right {
|
|
358
|
-
top:
|
|
359
|
-
right:
|
|
368
|
+
top: 10px;
|
|
369
|
+
right: 10px;
|
|
360
370
|
cursor: nesw-resize;
|
|
361
371
|
}
|
|
362
372
|
|
|
363
373
|
.resize-handle.bottom-left {
|
|
364
|
-
bottom:
|
|
365
|
-
left:
|
|
374
|
+
bottom: 10px;
|
|
375
|
+
left: 10px;
|
|
366
376
|
cursor: nesw-resize;
|
|
367
377
|
}
|
|
368
378
|
|
|
369
379
|
.resize-handle.bottom-right {
|
|
370
|
-
bottom:
|
|
371
|
-
right:
|
|
380
|
+
bottom: 10px;
|
|
381
|
+
right: 10px;
|
|
372
382
|
cursor: nwse-resize;
|
|
373
383
|
}
|
|
374
384
|
|