@z-api/call 1.0.0-staging.0
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 +590 -0
- package/dist/core/AudioEngine.d.ts +25 -0
- package/dist/core/AudioEngine.d.ts.map +1 -0
- package/dist/core/CallState.d.ts +15 -0
- package/dist/core/CallState.d.ts.map +1 -0
- package/dist/core/CallWebSocket.d.ts +29 -0
- package/dist/core/CallWebSocket.d.ts.map +1 -0
- package/dist/core/EventEmitter.d.ts +12 -0
- package/dist/core/EventEmitter.d.ts.map +1 -0
- package/dist/core/VideoEngine.d.ts +37 -0
- package/dist/core/VideoEngine.d.ts.map +1 -0
- package/dist/core/ZAPICallClient.d.ts +39 -0
- package/dist/core/ZAPICallClient.d.ts.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/types.d.ts +155 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/format.d.ts +5 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/pcm.d.ts +7 -0
- package/dist/utils/pcm.d.ts.map +1 -0
- package/dist/widget/CallWidget.css.d.ts +3 -0
- package/dist/widget/CallWidget.css.d.ts.map +1 -0
- package/dist/widget/CallWidget.d.ts +86 -0
- package/dist/widget/CallWidget.d.ts.map +1 -0
- package/dist/widget/icons.d.ts +16 -0
- package/dist/widget/icons.d.ts.map +1 -0
- package/dist/z-api-call.global.js +915 -0
- package/dist/z-api-call.global.js.map +1 -0
- package/dist/z-api-call.mjs +1830 -0
- package/dist/z-api-call.mjs.map +1 -0
- package/dist/z-api-call.umd.js +915 -0
- package/dist/z-api-call.umd.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,915 @@
|
|
|
1
|
+
(function(g,y){typeof exports=="object"&&typeof module<"u"?y(exports):typeof define=="function"&&define.amd?define(["exports"],y):(g=typeof globalThis<"u"?globalThis:g||self,y(g.ZAPICall={}))})(this,(function(g){"use strict";class y{constructor(){this.listeners=new Map}on(t,e){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(e),this}off(t,e){var a;return(a=this.listeners.get(t))==null||a.delete(e),this}emit(t,...e){const a=this.listeners.get(t);if(a)for(const i of a)try{i(...e)}catch(s){console.error(`[ZAPICall] Event listener error (${String(t)}):`,s)}}removeAllListeners(){this.listeners.clear()}}const N=1e3,F=3e4,U=25e3;class j{constructor(t,e,a){this.ws=null,this.reconnectMs=N,this.reconnectTimer=null,this.pingTimer=null,this.destroyed=!1,this.onJson=()=>{},this.onBinary=()=>{},this.onConnectionChange=()=>{},this.baseUrl=t,this.instanceId=e,this.getToken=a}buildUrl(t){const e=new URL(this.baseUrl);return e.protocol=e.protocol==="https:"?"wss:":"ws:",e.pathname="/ws/call",e.searchParams.set("instance",this.instanceId),e.searchParams.set("token",t),e.toString()}async connect(){if(this.destroyed)return;this.cleanup();let t;try{t=await this.getToken()}catch(i){console.warn("[ZAPICall] Failed to get token:",i),this.scheduleReconnect();return}if(this.destroyed)return;const e=this.buildUrl(t);console.log("[ZAPICall] Connecting to",e);const a=new WebSocket(e);a.binaryType="arraybuffer",this.ws=a,a.onopen=()=>{console.log("[ZAPICall] WebSocket connected"),this.reconnectMs=N,this.onConnectionChange(!0),this.startPing()},a.onmessage=i=>{if(typeof i.data=="string")try{const s=JSON.parse(i.data);console.log("[ZAPICall] <<",s.type,s),this.onJson(s)}catch{}else i.data instanceof ArrayBuffer&&this.onBinary(i.data)},a.onclose=i=>{if(console.log("[ZAPICall] WebSocket closed:",i.code,i.reason),this.stopPing(),this.onConnectionChange(!1),i.code===4001){console.warn("[ZAPICall] Invalid credentials (4001), not reconnecting");return}this.scheduleReconnect()},a.onerror=i=>{console.warn("[ZAPICall] WebSocket error:",i)}}send(t){var e;((e=this.ws)==null?void 0:e.readyState)===WebSocket.OPEN&&this.ws.send(t)}sendJson(t){this.send(JSON.stringify(t))}isConnected(){var t;return((t=this.ws)==null?void 0:t.readyState)===WebSocket.OPEN}destroy(){this.destroyed=!0,this.cleanup(),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}cleanup(){this.stopPing(),this.ws&&(this.ws.onopen=null,this.ws.onmessage=null,this.ws.onclose=null,this.ws.onerror=null,(this.ws.readyState===WebSocket.OPEN||this.ws.readyState===WebSocket.CONNECTING)&&this.ws.close(),this.ws=null)}scheduleReconnect(){this.destroyed||(this.reconnectTimer=setTimeout(()=>{this.connect()},this.reconnectMs),this.reconnectMs=Math.min(this.reconnectMs*2,F))}startPing(){this.pingTimer=setInterval(()=>{this.sendJson({type:"ping"})},U)}stopPing(){this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null)}}function q(c){const t=new Float32Array(c.length);for(let e=0;e<c.length;e++)t[e]=c[e]/32768;return t}function _(c){const t=new Int16Array(c.length);for(let e=0;e<c.length;e++){let a=c[e];a>1?a=1:a<-1&&(a=-1),t[e]=Math.round(a*32767)}return t}function W(c){if(c.length===0)return 0;let t=0;for(let e=0;e<c.length;e++)t+=c[e]*c[e];return Math.sqrt(t/c.length)}const z=.02,K=.15;class R{constructor(t=48e3){this.playbackCtx=null,this.micCtx=null,this.micStream=null,this.micProcessor=null,this.micSource=null,this.nextPlayTime=0,this.destroyed=!1,this.onMicData=()=>{},this.onAudioLevel=()=>{},this.playLogCount=0,this.sampleRate=t}playPcm(t){if(this.destroyed||t.byteLength===0)return;if(this.playbackCtx||(this.playbackCtx=new AudioContext({sampleRate:this.sampleRate}),this.nextPlayTime=this.playbackCtx.currentTime),this.playbackCtx.state==="suspended"&&this.playbackCtx.resume(),this.playLogCount++,this.playLogCount<=5||this.playLogCount%250===0){const r=new Int16Array(t);let l=0;for(let p=0;p<Math.min(r.length,100);p++){const u=Math.abs(r[p]);u>l&&(l=u)}console.log(`[AudioEngine] playPcm #${this.playLogCount} bytes=${t.byteLength} ctxState=${this.playbackCtx.state} sampleRate=${this.sampleRate} maxSample=${l} nextPlayT=${this.nextPlayTime.toFixed(3)} ctxTime=${this.playbackCtx.currentTime.toFixed(3)}`)}const e=new Int16Array(t),a=q(e),i=W(a);this.onAudioLevel(i);const s=this.playbackCtx.createBuffer(1,a.length,this.sampleRate);s.getChannelData(0).set(a);const n=this.playbackCtx.createBufferSource();n.buffer=s,n.connect(this.playbackCtx.destination);const o=this.playbackCtx.currentTime;this.nextPlayTime<o?this.nextPlayTime=o+z:this.nextPlayTime-o>K&&(this.nextPlayTime=o+z),n.start(this.nextPlayTime),this.nextPlayTime+=s.duration}async startMic(){this.destroyed||this.micStream||(this.micStream=await navigator.mediaDevices.getUserMedia({audio:{sampleRate:this.sampleRate,channelCount:1,echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0}}),this.micCtx=new AudioContext({sampleRate:this.sampleRate}),this.micSource=this.micCtx.createMediaStreamSource(this.micStream),this.micProcessor=this.micCtx.createScriptProcessor(4096,1,1),this.micProcessor.onaudioprocess=t=>{if(this.destroyed)return;const e=t.inputBuffer.getChannelData(0),a=_(e);this.onMicData(a.buffer)},this.micSource.connect(this.micProcessor),this.micProcessor.connect(this.micCtx.destination))}stopMic(){if(this.micProcessor&&(this.micProcessor.disconnect(),this.micProcessor=null),this.micSource&&(this.micSource.disconnect(),this.micSource=null),this.micStream){for(const t of this.micStream.getTracks())t.stop();this.micStream=null}this.micCtx&&(this.micCtx.close().catch(()=>{}),this.micCtx=null)}resume(){this.playbackCtx||(this.playbackCtx=new AudioContext({sampleRate:this.sampleRate}),this.nextPlayTime=this.playbackCtx.currentTime),this.playbackCtx.state==="suspended"&&this.playbackCtx.resume()}destroy(){this.destroyed=!0,this.stopMic(),this.playbackCtx&&(this.playbackCtx.close().catch(()=>{}),this.playbackCtx=null)}}const Z=100,G="avc1.42001E",J=5;class Y{constructor(){this.canvas=null,this.ctx=null,this.localVideo=null,this.localStream=null,this.captureCanvas=null,this.captureCtx=null,this.tempCanvas=null,this.tempCtx=null,this.captureInterval=null,this.h264Decoder=null,this.h264ErrorCount=0,this.orientationMap=new Map,this.onCameraFrame=null,this.onEncodedCameraFrame=null,this.h264Encoder=null,this.encoderKeyFrameInterval=3e3,this.lastKeyFrameTs=0,this.h264NeedKeyFrame=!1,this.h264GotKeyFrame=!1,this.h264FrameIndex=0}setRemoteCanvas(t){this.canvas=t,this.ctx=t.getContext("2d")}renderFrame(t){if(!this.canvas||!this.ctx)return;const{width:e,height:a,data:i,orientation:s,format:n,isKeyFrame:o,timestamp:r}=t;if(!(e<=0||a<=0)){if(n===Z){this.decodeH264Frame(i,e,a,s,o,r);return}this.renderNV12Frame(i,e,a,s)}}decodeH264Frame(t,e,a,i,s,n){if(t.byteLength===0)return;if(!this.h264GotKeyFrame){if(!s)return;this.h264GotKeyFrame=!0}if(!this.h264Decoder||this.h264Decoder.state==="closed"){if(this.h264ErrorCount>=J||(this.h264Decoder=this.createH264Decoder(),!this.h264Decoder)||(this.h264GotKeyFrame=!1,!s))return;this.h264GotKeyFrame=!0}this.h264FrameIndex++;const o=this.h264FrameIndex*66666;this.orientationMap.set(o,i);try{this.h264Decoder.decode(new EncodedVideoChunk({type:s?"key":"delta",timestamp:o,data:t}))}catch{this.h264ErrorCount++,this.h264GotKeyFrame=!1}}createH264Decoder(){if(typeof globalThis.VideoDecoder!="function")return null;try{const t=new VideoDecoder({output:e=>{try{if(!this.canvas||!this.ctx){e.close();return}const a=this.orientationMap.get(e.timestamp)??1;this.orientationMap.delete(e.timestamp);const i=e.codedWidth,s=e.codedHeight,n=a===2||a===4,o=n?s:i,r=n?i:s;(this.canvas.width!==o||this.canvas.height!==r)&&(this.canvas.width=o,this.canvas.height=r),this.ctx.save(),a===2?(this.ctx.translate(o,0),this.ctx.rotate(Math.PI/2)):a===3?(this.ctx.translate(o,r),this.ctx.rotate(Math.PI)):a===4&&(this.ctx.translate(0,r),this.ctx.rotate(-Math.PI/2)),this.ctx.drawImage(e,0,0),this.ctx.restore()}finally{e.close()}},error:()=>{this.h264ErrorCount++,this.orientationMap.clear()}});return t.configure({codec:G,optimizeForLatency:!0}),this.h264ErrorCount=0,t}catch{return null}}renderNV12Frame(t,e,a,i){if(!this.canvas||!this.ctx)return;const s=i===2||i===4,n=s?a:e,o=s?e:a;if((this.canvas.width!==n||this.canvas.height!==o)&&(this.canvas.width=n,this.canvas.height=o),typeof globalThis.VideoFrame=="function")try{const u=new globalThis.VideoFrame(new Uint8Array(t),{format:"NV12",codedWidth:e,codedHeight:a,timestamp:0});this.ctx.save(),i===2?(this.ctx.translate(n,0),this.ctx.rotate(Math.PI/2)):i===3?(this.ctx.translate(n,o),this.ctx.rotate(Math.PI)):i===4&&(this.ctx.translate(0,o),this.ctx.rotate(-Math.PI/2)),this.ctx.drawImage(u,0,0),this.ctx.restore(),u.close();return}catch{}const r=new Uint8Array(t),l=this.nv12ToRgba(r,e,a),p=new ImageData(new Uint8ClampedArray(l.buffer),e,a);i>=2&&i<=4?(this.tempCanvas||(this.tempCanvas=document.createElement("canvas"),this.tempCtx=this.tempCanvas.getContext("2d")),this.tempCanvas.width=e,this.tempCanvas.height=a,this.tempCtx.putImageData(p,0,0),this.ctx.save(),i===2?(this.ctx.translate(n,0),this.ctx.rotate(Math.PI/2)):i===3?(this.ctx.translate(n,o),this.ctx.rotate(Math.PI)):i===4&&(this.ctx.translate(0,o),this.ctx.rotate(-Math.PI/2)),this.ctx.drawImage(this.tempCanvas,0,0),this.ctx.restore()):this.ctx.putImageData(p,0,0)}async startCamera(t,e=640,a=480,i=15){this.localVideo=t;const s=await navigator.mediaDevices.getUserMedia({video:{width:{ideal:e},height:{ideal:a},frameRate:{ideal:i}},audio:!1});this.localStream=s,t.srcObject=s,t.muted=!0,await t.play(),this.canUseH264Encoder()?this.startH264Capture(s,e,a,i):(this.captureCanvas=document.createElement("canvas"),this.captureCanvas.width=e,this.captureCanvas.height=a,this.captureCtx=this.captureCanvas.getContext("2d",{willReadFrequently:!0}),this.captureInterval=setInterval(()=>this.captureFrameRaw(),Math.floor(1e3/i)))}stopCamera(){if(this.captureInterval&&(clearInterval(this.captureInterval),this.captureInterval=null),this.h264Encoder&&this.h264Encoder.state!=="closed")try{this.h264Encoder.close()}catch{}this.h264Encoder=null,this.localStream&&(this.localStream.getTracks().forEach(t=>t.stop()),this.localStream=null),this.localVideo&&(this.localVideo.srcObject=null,this.localVideo=null),this.captureCanvas=null,this.captureCtx=null}canUseH264Encoder(){return typeof globalThis.VideoEncoder=="function"&&typeof globalThis.MediaStreamTrackProcessor=="function"}startH264Capture(t,e,a,i){const s=t.getVideoTracks()[0];if(!s)return;let n=e,o=a,r=0;const l=new Map;this.h264Encoder=new VideoEncoder({output:(d,m)=>{var h,v;if(!this.onEncodedCameraFrame)return;const f=new ArrayBuffer(d.byteLength);d.copyTo(f);const b=d.type==="key",C=((h=m==null?void 0:m.decoderConfig)==null?void 0:h.codedWidth)??n,M=((v=m==null?void 0:m.decoderConfig)==null?void 0:v.codedHeight)??o;let w=0;const k=l.get(d.timestamp);k!=null&&(l.delete(d.timestamp),w=Date.now()-k);const L=d.timestamp/1e3;this.onEncodedCameraFrame(f,C,M,L,b,w)},error:d=>{console.error("[VideoEngine] encoder error:",d)}}),this.h264Encoder.configure({codec:"avc1.42001f",width:n,height:o,bitrate:5e5,framerate:i,latencyMode:"realtime",bitrateMode:"constant",avc:{format:"annexb"}});const u=new globalThis.MediaStreamTrackProcessor({track:s}).readable.getReader();(async()=>{for(;this.h264Encoder&&this.h264Encoder.state!=="closed";)try{const{value:d,done:m}=await u.read();if(m||!d)break;if(this.h264Encoder.state!=="configured"){d.close();continue}const f=Date.now(),b=f-this.lastKeyFrameTs,C=this.h264NeedKeyFrame||b>=this.encoderKeyFrameInterval||r===0;l.set(d.timestamp,f),this.h264Encoder.encode(d,{keyFrame:C}),d.close(),r++,C&&(this.lastKeyFrameTs=f,this.h264NeedKeyFrame=!1)}catch{break}})()}captureFrameRaw(){if(!this.captureCtx||!this.captureCanvas||!this.localVideo||!this.onCameraFrame||this.localVideo.readyState<2)return;const t=this.captureCanvas.width,e=this.captureCanvas.height;this.captureCtx.drawImage(this.localVideo,0,0,t,e);const a=this.captureCtx.getImageData(0,0,t,e);this.onCameraFrame(a.data.buffer,t,e)}nv12ToRgba(t,e,a){const i=new Uint8ClampedArray(e*a*4),s=e*a;for(let n=0;n<a;n++)for(let o=0;o<e;o++){const r=n*e+o,l=s+(n>>1)*e+(o&-2),p=t[r],u=t[l]-128,x=t[l+1]-128,d=r*4;i[d]=T(p+1.402*x),i[d+1]=T(p-.344*u-.714*x),i[d+2]=T(p+1.772*u),i[d+3]=255}return i}clearCanvas(){this.canvas&&this.ctx&&this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)}destroy(){if(this.stopCamera(),this.h264Decoder&&this.h264Decoder.state!=="closed")try{this.h264Decoder.close()}catch{}this.h264Decoder=null,this.orientationMap.clear(),this.canvas=null,this.ctx=null}}function T(c){return c<0?0:c>255?255:c|0}class X{constructor(){this.calls=new Map}addCall(t){this.calls.set(t.callId,{...t})}updateState(t,e){const a=this.calls.get(t);a&&(a.state=e,e==="ended"&&this.calls.delete(t))}updateIsVideo(t,e){const a=this.calls.get(t);a&&(a.isVideo=e)}removeCall(t){this.calls.delete(t)}getCall(t){return this.calls.get(t)}getActiveCall(){for(const t of this.calls.values())if(t.state==="accepted"||t.state==="active")return t}getRingingCalls(){const t=[];for(const e of this.calls.values())(e.state==="ringing"||e.state==="preaccepted")&&t.push(e);return t}getAllCalls(){return Array.from(this.calls.values())}hasActiveCalls(){for(const t of this.calls.values())if(t.state!=="ended")return!0;return!1}clear(){this.calls.clear()}}function Q(c){const t=c.mode==="light",e=c.primaryColor||"#00a884",a=c.dangerColor||"#f44336",i=c.backgroundColor||(t?"#f0f2f5":"#202c33"),s=c.surfaceColor||(t?"#ffffff":"#111b21"),n=c.textColor||(t?"#111b21":"#e9edef"),o=c.textSecondaryColor||(t?"#667781":"#8696a0"),r=c.borderRadius||"16px",l=c.fontFamily||"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",p=t?"rgba(0,0,0,0.06)":"rgba(255,255,255,0.08)",u=t?"rgba(0,0,0,0.1)":"rgba(255,255,255,0.1)",x=t?"rgba(0,0,0,0.14)":"rgba(255,255,255,0.15)",d=t?"rgba(0,0,0,0.12)":"rgba(0,0,0,0.3)",m=t?"rgba(0,0,0,0.16)":"rgba(0,0,0,0.4)",f=t?"rgba(0,0,0,0.08)":"rgba(255,255,255,0.06)",b=t?"rgba(0,0,0,0.12)":"rgba(255,255,255,0.1)";return`
|
|
2
|
+
:host {
|
|
3
|
+
all: initial;
|
|
4
|
+
font-family: ${l};
|
|
5
|
+
color: ${n};
|
|
6
|
+
--primary: ${e};
|
|
7
|
+
--danger: ${a};
|
|
8
|
+
--bg: ${i};
|
|
9
|
+
--surface: ${s};
|
|
10
|
+
--text: ${n};
|
|
11
|
+
--text-sec: ${o};
|
|
12
|
+
--radius: ${r};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
16
|
+
|
|
17
|
+
.zapi-call-root {
|
|
18
|
+
position: fixed;
|
|
19
|
+
z-index: 2147483647;
|
|
20
|
+
font-family: ${l};
|
|
21
|
+
}
|
|
22
|
+
.zapi-call-root.bottom-right { bottom: 20px; right: 20px; }
|
|
23
|
+
.zapi-call-root.bottom-left { bottom: 20px; left: 20px; }
|
|
24
|
+
.zapi-call-root.top-right { top: 20px; right: 20px; }
|
|
25
|
+
.zapi-call-root.top-left { top: 20px; left: 20px; }
|
|
26
|
+
|
|
27
|
+
/* Bubble */
|
|
28
|
+
.bubble {
|
|
29
|
+
width: 56px;
|
|
30
|
+
height: 56px;
|
|
31
|
+
border-radius: 50%;
|
|
32
|
+
background: var(--primary);
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: center;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
box-shadow: 0 4px 12px ${d};
|
|
38
|
+
transition: transform 0.2s, box-shadow 0.2s;
|
|
39
|
+
position: relative;
|
|
40
|
+
user-select: none;
|
|
41
|
+
}
|
|
42
|
+
.bubble:hover { transform: scale(1.08); box-shadow: 0 6px 20px ${m}; }
|
|
43
|
+
.bubble:active { transform: scale(0.95); }
|
|
44
|
+
.bubble svg { width: 26px; height: 26px; color: #fff; }
|
|
45
|
+
|
|
46
|
+
.bubble .badge {
|
|
47
|
+
position: absolute;
|
|
48
|
+
top: -2px;
|
|
49
|
+
right: -2px;
|
|
50
|
+
width: 18px;
|
|
51
|
+
height: 18px;
|
|
52
|
+
border-radius: 50%;
|
|
53
|
+
background: var(--danger);
|
|
54
|
+
color: #fff;
|
|
55
|
+
font-size: 11px;
|
|
56
|
+
font-weight: 700;
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
justify-content: center;
|
|
60
|
+
display: none;
|
|
61
|
+
}
|
|
62
|
+
.bubble .badge.visible { display: flex; }
|
|
63
|
+
|
|
64
|
+
.bubble .conn-dot {
|
|
65
|
+
position: absolute;
|
|
66
|
+
bottom: 2px;
|
|
67
|
+
right: 2px;
|
|
68
|
+
width: 10px;
|
|
69
|
+
height: 10px;
|
|
70
|
+
border-radius: 50%;
|
|
71
|
+
background: #f44336;
|
|
72
|
+
border: 2px solid var(--primary);
|
|
73
|
+
}
|
|
74
|
+
.bubble .conn-dot.connected { background: #4caf50; }
|
|
75
|
+
|
|
76
|
+
.bubble.ringing {
|
|
77
|
+
animation: bubble-pulse 1.5s ease-in-out infinite;
|
|
78
|
+
}
|
|
79
|
+
@keyframes bubble-pulse {
|
|
80
|
+
0%, 100% { box-shadow: 0 4px 12px ${d}; }
|
|
81
|
+
50% { box-shadow: 0 4px 12px ${d}, 0 0 0 12px rgba(0,168,132,0.2); }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Panel */
|
|
85
|
+
.panel {
|
|
86
|
+
position: absolute;
|
|
87
|
+
bottom: 66px;
|
|
88
|
+
right: 0;
|
|
89
|
+
width: 360px;
|
|
90
|
+
max-height: 560px;
|
|
91
|
+
background: var(--surface);
|
|
92
|
+
border-radius: var(--radius);
|
|
93
|
+
box-shadow: 0 8px 32px ${m};
|
|
94
|
+
overflow: hidden;
|
|
95
|
+
transform: translateY(10px);
|
|
96
|
+
opacity: 0;
|
|
97
|
+
pointer-events: none;
|
|
98
|
+
transition: transform 0.25s ease, opacity 0.25s ease;
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: column;
|
|
101
|
+
}
|
|
102
|
+
.panel.open {
|
|
103
|
+
transform: translateY(0);
|
|
104
|
+
opacity: 1;
|
|
105
|
+
pointer-events: auto;
|
|
106
|
+
}
|
|
107
|
+
.zapi-call-root.bottom-left .panel { right: auto; left: 0; }
|
|
108
|
+
.zapi-call-root.top-right .panel { bottom: auto; top: 66px; }
|
|
109
|
+
.zapi-call-root.top-left .panel { bottom: auto; top: 66px; right: auto; left: 0; }
|
|
110
|
+
|
|
111
|
+
/* Panel header with tabs */
|
|
112
|
+
.panel-header {
|
|
113
|
+
background: var(--bg);
|
|
114
|
+
border-bottom: 1px solid ${f};
|
|
115
|
+
flex-shrink: 0;
|
|
116
|
+
}
|
|
117
|
+
.panel-header-top {
|
|
118
|
+
padding: 10px 16px;
|
|
119
|
+
display: flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
justify-content: space-between;
|
|
122
|
+
}
|
|
123
|
+
.panel-header-top h3 {
|
|
124
|
+
font-size: 15px;
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
color: var(--text);
|
|
127
|
+
}
|
|
128
|
+
.panel-header .close-btn {
|
|
129
|
+
width: 28px;
|
|
130
|
+
height: 28px;
|
|
131
|
+
border: none;
|
|
132
|
+
background: transparent;
|
|
133
|
+
color: var(--text-sec);
|
|
134
|
+
cursor: pointer;
|
|
135
|
+
border-radius: 50%;
|
|
136
|
+
display: flex;
|
|
137
|
+
align-items: center;
|
|
138
|
+
justify-content: center;
|
|
139
|
+
transition: background 0.2s;
|
|
140
|
+
}
|
|
141
|
+
.panel-header .close-btn:hover { background: ${p}; }
|
|
142
|
+
.panel-header .close-btn svg { width: 18px; height: 18px; }
|
|
143
|
+
|
|
144
|
+
/* Tabs */
|
|
145
|
+
.tabs {
|
|
146
|
+
display: flex;
|
|
147
|
+
padding: 0 8px;
|
|
148
|
+
}
|
|
149
|
+
.tab {
|
|
150
|
+
flex: 1;
|
|
151
|
+
padding: 10px 8px;
|
|
152
|
+
border: none;
|
|
153
|
+
background: transparent;
|
|
154
|
+
color: var(--text-sec);
|
|
155
|
+
font-size: 13px;
|
|
156
|
+
font-weight: 600;
|
|
157
|
+
cursor: pointer;
|
|
158
|
+
position: relative;
|
|
159
|
+
display: flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
justify-content: center;
|
|
162
|
+
gap: 6px;
|
|
163
|
+
transition: color 0.2s;
|
|
164
|
+
font-family: inherit;
|
|
165
|
+
}
|
|
166
|
+
.tab svg { width: 16px; height: 16px; }
|
|
167
|
+
.tab:hover { color: var(--text); }
|
|
168
|
+
.tab.active { color: var(--primary); }
|
|
169
|
+
.tab.active::after {
|
|
170
|
+
content: '';
|
|
171
|
+
position: absolute;
|
|
172
|
+
bottom: 0;
|
|
173
|
+
left: 16px;
|
|
174
|
+
right: 16px;
|
|
175
|
+
height: 2px;
|
|
176
|
+
background: var(--primary);
|
|
177
|
+
border-radius: 2px 2px 0 0;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* Panel body / views */
|
|
181
|
+
.panel-body {
|
|
182
|
+
flex: 1;
|
|
183
|
+
overflow-y: auto;
|
|
184
|
+
overflow-x: hidden;
|
|
185
|
+
min-height: 0;
|
|
186
|
+
}
|
|
187
|
+
.view {
|
|
188
|
+
display: none;
|
|
189
|
+
animation: view-in 0.2s ease;
|
|
190
|
+
}
|
|
191
|
+
.view.active { display: block; }
|
|
192
|
+
@keyframes view-in {
|
|
193
|
+
from { opacity: 0; transform: translateY(6px); }
|
|
194
|
+
to { opacity: 1; transform: translateY(0); }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* ── Dialer view ── */
|
|
198
|
+
.dialer {
|
|
199
|
+
padding: 8px 16px 14px;
|
|
200
|
+
display: flex;
|
|
201
|
+
flex-direction: column;
|
|
202
|
+
align-items: center;
|
|
203
|
+
}
|
|
204
|
+
.dialer-display {
|
|
205
|
+
width: 100%;
|
|
206
|
+
height: 56px;
|
|
207
|
+
display: flex;
|
|
208
|
+
align-items: center;
|
|
209
|
+
justify-content: center;
|
|
210
|
+
position: relative;
|
|
211
|
+
margin-bottom: 8px;
|
|
212
|
+
}
|
|
213
|
+
.dialer-input {
|
|
214
|
+
width: 100%;
|
|
215
|
+
background: transparent;
|
|
216
|
+
border: none;
|
|
217
|
+
padding: 0 40px;
|
|
218
|
+
color: var(--text);
|
|
219
|
+
font-size: 28px;
|
|
220
|
+
font-weight: 300;
|
|
221
|
+
font-family: inherit;
|
|
222
|
+
text-align: center;
|
|
223
|
+
letter-spacing: 1.5px;
|
|
224
|
+
outline: none;
|
|
225
|
+
font-variant-numeric: tabular-nums;
|
|
226
|
+
caret-color: var(--primary);
|
|
227
|
+
}
|
|
228
|
+
.dialer-input::placeholder { color: var(--text-sec); font-size: 16px; font-weight: 400; letter-spacing: 0; }
|
|
229
|
+
.dialer-input.error::placeholder { color: #e74c3c; }
|
|
230
|
+
.backspace-btn {
|
|
231
|
+
position: absolute;
|
|
232
|
+
right: 0;
|
|
233
|
+
width: 36px;
|
|
234
|
+
height: 36px;
|
|
235
|
+
border: none;
|
|
236
|
+
background: transparent;
|
|
237
|
+
color: var(--text-sec);
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
border-radius: 50%;
|
|
240
|
+
display: flex;
|
|
241
|
+
align-items: center;
|
|
242
|
+
justify-content: center;
|
|
243
|
+
transition: background 0.2s, color 0.2s;
|
|
244
|
+
}
|
|
245
|
+
.backspace-btn:hover { background: ${p}; color: var(--text); }
|
|
246
|
+
.backspace-btn svg { width: 20px; height: 20px; }
|
|
247
|
+
|
|
248
|
+
/* Numpad */
|
|
249
|
+
.numpad {
|
|
250
|
+
display: grid;
|
|
251
|
+
grid-template-columns: repeat(3, 1fr);
|
|
252
|
+
gap: 6px;
|
|
253
|
+
width: 100%;
|
|
254
|
+
max-width: 240px;
|
|
255
|
+
margin-bottom: 12px;
|
|
256
|
+
}
|
|
257
|
+
.numpad-key {
|
|
258
|
+
width: 60px;
|
|
259
|
+
height: 60px;
|
|
260
|
+
border-radius: 50%;
|
|
261
|
+
border: none;
|
|
262
|
+
background: var(--bg);
|
|
263
|
+
color: var(--text);
|
|
264
|
+
font-size: 22px;
|
|
265
|
+
font-weight: 400;
|
|
266
|
+
cursor: pointer;
|
|
267
|
+
display: flex;
|
|
268
|
+
flex-direction: column;
|
|
269
|
+
align-items: center;
|
|
270
|
+
justify-content: center;
|
|
271
|
+
transition: background 0.15s, transform 0.1s;
|
|
272
|
+
user-select: none;
|
|
273
|
+
font-family: inherit;
|
|
274
|
+
justify-self: center;
|
|
275
|
+
line-height: 1;
|
|
276
|
+
}
|
|
277
|
+
.numpad-key:hover { background: ${u}; }
|
|
278
|
+
.numpad-key:active { transform: scale(0.92); background: ${x}; }
|
|
279
|
+
.numpad-key .sub {
|
|
280
|
+
font-size: 8px;
|
|
281
|
+
color: var(--text-sec);
|
|
282
|
+
letter-spacing: 1.5px;
|
|
283
|
+
margin-top: 1px;
|
|
284
|
+
font-weight: 600;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* Call buttons */
|
|
288
|
+
.call-btns {
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
gap: 16px;
|
|
292
|
+
}
|
|
293
|
+
.call-btn {
|
|
294
|
+
width: 56px;
|
|
295
|
+
height: 56px;
|
|
296
|
+
border-radius: 50%;
|
|
297
|
+
border: none;
|
|
298
|
+
background: var(--primary);
|
|
299
|
+
color: #fff;
|
|
300
|
+
cursor: pointer;
|
|
301
|
+
display: flex;
|
|
302
|
+
align-items: center;
|
|
303
|
+
justify-content: center;
|
|
304
|
+
box-shadow: 0 4px 16px rgba(0,168,132,0.3);
|
|
305
|
+
transition: transform 0.15s, box-shadow 0.15s;
|
|
306
|
+
}
|
|
307
|
+
.call-btn:hover { transform: scale(1.08); box-shadow: 0 6px 24px rgba(0,168,132,0.4); }
|
|
308
|
+
.call-btn:active { transform: scale(0.95); }
|
|
309
|
+
.call-btn svg { width: 26px; height: 26px; }
|
|
310
|
+
.call-btn:disabled {
|
|
311
|
+
opacity: 0.5;
|
|
312
|
+
cursor: not-allowed;
|
|
313
|
+
transform: none;
|
|
314
|
+
box-shadow: 0 4px 16px rgba(0,168,132,0.15);
|
|
315
|
+
}
|
|
316
|
+
.video-call-btn {
|
|
317
|
+
background: #2196f3;
|
|
318
|
+
box-shadow: 0 4px 16px rgba(33,150,243,0.3);
|
|
319
|
+
}
|
|
320
|
+
.video-call-btn:hover { box-shadow: 0 6px 24px rgba(33,150,243,0.4); }
|
|
321
|
+
|
|
322
|
+
/* ── Calls view ── */
|
|
323
|
+
.calls-view {
|
|
324
|
+
padding: 8px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.empty-state {
|
|
328
|
+
padding: 40px 16px;
|
|
329
|
+
text-align: center;
|
|
330
|
+
color: var(--text-sec);
|
|
331
|
+
font-size: 13px;
|
|
332
|
+
}
|
|
333
|
+
.empty-state .icon {
|
|
334
|
+
font-size: 36px;
|
|
335
|
+
margin-bottom: 8px;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* Call Card */
|
|
339
|
+
.call-card {
|
|
340
|
+
background: var(--bg);
|
|
341
|
+
border-radius: 12px;
|
|
342
|
+
padding: 14px;
|
|
343
|
+
margin-bottom: 8px;
|
|
344
|
+
animation: card-in 0.3s ease;
|
|
345
|
+
}
|
|
346
|
+
.call-card.ended {
|
|
347
|
+
opacity: 0.5;
|
|
348
|
+
animation: card-out 0.5s ease forwards;
|
|
349
|
+
}
|
|
350
|
+
@keyframes card-in {
|
|
351
|
+
from { transform: translateY(-8px); opacity: 0; }
|
|
352
|
+
to { transform: translateY(0); opacity: 1; }
|
|
353
|
+
}
|
|
354
|
+
@keyframes card-out {
|
|
355
|
+
to { transform: translateY(8px); opacity: 0; }
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.call-card-top {
|
|
359
|
+
display: flex;
|
|
360
|
+
align-items: center;
|
|
361
|
+
gap: 10px;
|
|
362
|
+
margin-bottom: 10px;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.avatar {
|
|
366
|
+
width: 40px;
|
|
367
|
+
height: 40px;
|
|
368
|
+
border-radius: 50%;
|
|
369
|
+
background: var(--surface);
|
|
370
|
+
display: flex;
|
|
371
|
+
align-items: center;
|
|
372
|
+
justify-content: center;
|
|
373
|
+
flex-shrink: 0;
|
|
374
|
+
}
|
|
375
|
+
.avatar svg { width: 22px; height: 22px; color: var(--text-sec); }
|
|
376
|
+
.avatar .avatar-img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
|
|
377
|
+
|
|
378
|
+
.call-info { flex: 1; min-width: 0; }
|
|
379
|
+
.call-phone {
|
|
380
|
+
font-size: 15px;
|
|
381
|
+
font-weight: 600;
|
|
382
|
+
color: var(--text);
|
|
383
|
+
white-space: nowrap;
|
|
384
|
+
overflow: hidden;
|
|
385
|
+
text-overflow: ellipsis;
|
|
386
|
+
}
|
|
387
|
+
.call-meta {
|
|
388
|
+
font-size: 12px;
|
|
389
|
+
color: var(--text-sec);
|
|
390
|
+
display: flex;
|
|
391
|
+
align-items: center;
|
|
392
|
+
gap: 6px;
|
|
393
|
+
margin-top: 2px;
|
|
394
|
+
}
|
|
395
|
+
.call-meta svg { width: 13px; height: 13px; }
|
|
396
|
+
|
|
397
|
+
.state-badge {
|
|
398
|
+
padding: 3px 8px;
|
|
399
|
+
border-radius: 6px;
|
|
400
|
+
font-size: 11px;
|
|
401
|
+
font-weight: 700;
|
|
402
|
+
text-transform: uppercase;
|
|
403
|
+
letter-spacing: 0.5px;
|
|
404
|
+
flex-shrink: 0;
|
|
405
|
+
}
|
|
406
|
+
.state-badge.ringing {
|
|
407
|
+
background: #f9a825;
|
|
408
|
+
color: #000;
|
|
409
|
+
animation: badge-pulse 1.5s ease-in-out infinite;
|
|
410
|
+
}
|
|
411
|
+
.state-badge.preaccepted { background: #ff9800; color: #000; }
|
|
412
|
+
.state-badge.accepted { background: #2196f3; color: #fff; }
|
|
413
|
+
.state-badge.active { background: var(--primary); color: #fff; }
|
|
414
|
+
.state-badge.ended { background: var(--danger); color: #fff; }
|
|
415
|
+
@keyframes badge-pulse {
|
|
416
|
+
0%, 100% { opacity: 1; }
|
|
417
|
+
50% { opacity: 0.5; }
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.timer {
|
|
421
|
+
font-size: 22px;
|
|
422
|
+
font-weight: 300;
|
|
423
|
+
text-align: center;
|
|
424
|
+
margin: 8px 0;
|
|
425
|
+
font-variant-numeric: tabular-nums;
|
|
426
|
+
color: var(--text);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.call-actions {
|
|
430
|
+
display: flex;
|
|
431
|
+
gap: 8px;
|
|
432
|
+
margin-top: 8px;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.btn {
|
|
436
|
+
flex: 1;
|
|
437
|
+
padding: 10px 16px;
|
|
438
|
+
border: none;
|
|
439
|
+
border-radius: 24px;
|
|
440
|
+
font-size: 14px;
|
|
441
|
+
font-weight: 600;
|
|
442
|
+
cursor: pointer;
|
|
443
|
+
transition: transform 0.15s, opacity 0.15s;
|
|
444
|
+
display: flex;
|
|
445
|
+
align-items: center;
|
|
446
|
+
justify-content: center;
|
|
447
|
+
gap: 6px;
|
|
448
|
+
font-family: inherit;
|
|
449
|
+
}
|
|
450
|
+
.btn:hover { transform: scale(1.03); }
|
|
451
|
+
.btn:active { transform: scale(0.97); }
|
|
452
|
+
.btn svg { width: 18px; height: 18px; }
|
|
453
|
+
.btn-accept { background: var(--primary); color: #fff; }
|
|
454
|
+
.btn-reject { background: var(--danger); color: #fff; }
|
|
455
|
+
.btn-mute {
|
|
456
|
+
width: 44px;
|
|
457
|
+
height: 44px;
|
|
458
|
+
flex: none;
|
|
459
|
+
border-radius: 50%;
|
|
460
|
+
background: var(--bg);
|
|
461
|
+
border: 1px solid ${b};
|
|
462
|
+
color: var(--text);
|
|
463
|
+
padding: 0;
|
|
464
|
+
display: flex;
|
|
465
|
+
align-items: center;
|
|
466
|
+
justify-content: center;
|
|
467
|
+
cursor: pointer;
|
|
468
|
+
transition: background 0.2s;
|
|
469
|
+
}
|
|
470
|
+
.btn-mute:hover { background: ${p}; }
|
|
471
|
+
.btn-mute.muted { background: var(--danger); color: #fff; border-color: transparent; }
|
|
472
|
+
.btn-mute svg { width: 20px; height: 20px; }
|
|
473
|
+
|
|
474
|
+
/* ── In-Call view ── */
|
|
475
|
+
.incall-view {
|
|
476
|
+
display: flex;
|
|
477
|
+
flex-direction: column;
|
|
478
|
+
align-items: center;
|
|
479
|
+
text-align: center;
|
|
480
|
+
}
|
|
481
|
+
.incall-view .audio-call-ui {
|
|
482
|
+
padding: 24px 16px;
|
|
483
|
+
}
|
|
484
|
+
.incall-avatar {
|
|
485
|
+
width: 80px;
|
|
486
|
+
height: 80px;
|
|
487
|
+
border-radius: 50%;
|
|
488
|
+
background: var(--bg);
|
|
489
|
+
display: flex;
|
|
490
|
+
align-items: center;
|
|
491
|
+
justify-content: center;
|
|
492
|
+
margin-bottom: 16px;
|
|
493
|
+
}
|
|
494
|
+
.incall-avatar svg { width: 40px; height: 40px; color: var(--text-sec); }
|
|
495
|
+
.incall-avatar .avatar-img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
|
|
496
|
+
.incall-phone {
|
|
497
|
+
font-size: 20px;
|
|
498
|
+
font-weight: 600;
|
|
499
|
+
color: var(--text);
|
|
500
|
+
margin-bottom: 4px;
|
|
501
|
+
}
|
|
502
|
+
.incall-state {
|
|
503
|
+
font-size: 14px;
|
|
504
|
+
color: var(--text-sec);
|
|
505
|
+
margin-bottom: 4px;
|
|
506
|
+
}
|
|
507
|
+
.incall-timer {
|
|
508
|
+
font-size: 28px;
|
|
509
|
+
font-weight: 300;
|
|
510
|
+
color: var(--text);
|
|
511
|
+
font-variant-numeric: tabular-nums;
|
|
512
|
+
margin-bottom: 20px;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/* Audio level bar */
|
|
516
|
+
.audio-level-wrap {
|
|
517
|
+
width: 100%;
|
|
518
|
+
max-width: 200px;
|
|
519
|
+
height: 6px;
|
|
520
|
+
background: var(--bg);
|
|
521
|
+
border-radius: 3px;
|
|
522
|
+
overflow: hidden;
|
|
523
|
+
margin-bottom: 28px;
|
|
524
|
+
}
|
|
525
|
+
.audio-level-bar {
|
|
526
|
+
height: 100%;
|
|
527
|
+
width: 0%;
|
|
528
|
+
background: var(--primary);
|
|
529
|
+
border-radius: 3px;
|
|
530
|
+
transition: width 0.1s ease;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/* In-call controls */
|
|
534
|
+
.incall-controls {
|
|
535
|
+
display: flex;
|
|
536
|
+
align-items: center;
|
|
537
|
+
gap: 32px;
|
|
538
|
+
}
|
|
539
|
+
.incall-btn {
|
|
540
|
+
width: 56px;
|
|
541
|
+
height: 56px;
|
|
542
|
+
border-radius: 50%;
|
|
543
|
+
border: none;
|
|
544
|
+
display: flex;
|
|
545
|
+
align-items: center;
|
|
546
|
+
justify-content: center;
|
|
547
|
+
cursor: pointer;
|
|
548
|
+
transition: transform 0.15s;
|
|
549
|
+
}
|
|
550
|
+
.incall-btn:hover { transform: scale(1.08); }
|
|
551
|
+
.incall-btn:active { transform: scale(0.95); }
|
|
552
|
+
.incall-btn svg { width: 24px; height: 24px; }
|
|
553
|
+
.incall-btn-mute {
|
|
554
|
+
background: var(--bg);
|
|
555
|
+
color: var(--text);
|
|
556
|
+
border: 1px solid ${b};
|
|
557
|
+
}
|
|
558
|
+
.incall-btn-mute.muted {
|
|
559
|
+
background: var(--danger);
|
|
560
|
+
color: #fff;
|
|
561
|
+
border-color: transparent;
|
|
562
|
+
}
|
|
563
|
+
.incall-btn-camera {
|
|
564
|
+
background: var(--bg);
|
|
565
|
+
color: var(--text);
|
|
566
|
+
border: 1px solid ${b};
|
|
567
|
+
}
|
|
568
|
+
.incall-btn-camera:hover { background: ${p}; }
|
|
569
|
+
.incall-btn-camera.muted {
|
|
570
|
+
color: var(--text-sec);
|
|
571
|
+
}
|
|
572
|
+
.incall-btn-end {
|
|
573
|
+
background: var(--danger);
|
|
574
|
+
color: #fff;
|
|
575
|
+
width: 64px;
|
|
576
|
+
height: 64px;
|
|
577
|
+
}
|
|
578
|
+
.incall-btn-end svg { width: 28px; height: 28px; }
|
|
579
|
+
|
|
580
|
+
/* ── Video Call ── */
|
|
581
|
+
.audio-call-ui {
|
|
582
|
+
display: flex;
|
|
583
|
+
flex-direction: column;
|
|
584
|
+
align-items: center;
|
|
585
|
+
}
|
|
586
|
+
.video-container {
|
|
587
|
+
position: relative;
|
|
588
|
+
width: 100%;
|
|
589
|
+
height: 420px;
|
|
590
|
+
overflow: hidden;
|
|
591
|
+
background: #000;
|
|
592
|
+
display: flex;
|
|
593
|
+
align-items: center;
|
|
594
|
+
justify-content: center;
|
|
595
|
+
border-radius: 0 0 12px 12px;
|
|
596
|
+
}
|
|
597
|
+
.remote-video {
|
|
598
|
+
max-width: 100%;
|
|
599
|
+
max-height: 100%;
|
|
600
|
+
display: block;
|
|
601
|
+
object-fit: contain;
|
|
602
|
+
}
|
|
603
|
+
.video-overlay-top {
|
|
604
|
+
position: absolute;
|
|
605
|
+
top: 0; left: 0; right: 0;
|
|
606
|
+
padding: 16px;
|
|
607
|
+
background: linear-gradient(to bottom, rgba(0,0,0,0.6), transparent);
|
|
608
|
+
z-index: 2;
|
|
609
|
+
}
|
|
610
|
+
.video-caller-info {
|
|
611
|
+
display: flex;
|
|
612
|
+
align-items: center;
|
|
613
|
+
justify-content: space-between;
|
|
614
|
+
}
|
|
615
|
+
.video-phone {
|
|
616
|
+
font-size: 15px;
|
|
617
|
+
font-weight: 600;
|
|
618
|
+
color: #fff;
|
|
619
|
+
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
|
|
620
|
+
}
|
|
621
|
+
.video-timer {
|
|
622
|
+
font-size: 14px;
|
|
623
|
+
font-weight: 500;
|
|
624
|
+
color: rgba(255,255,255,0.85);
|
|
625
|
+
font-variant-numeric: tabular-nums;
|
|
626
|
+
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
|
|
627
|
+
}
|
|
628
|
+
.video-overlay-bottom {
|
|
629
|
+
position: absolute;
|
|
630
|
+
bottom: 0; left: 0; right: 0;
|
|
631
|
+
padding: 20px;
|
|
632
|
+
background: linear-gradient(to top, rgba(0,0,0,0.6), transparent);
|
|
633
|
+
display: flex;
|
|
634
|
+
align-items: center;
|
|
635
|
+
justify-content: center;
|
|
636
|
+
gap: 32px;
|
|
637
|
+
z-index: 2;
|
|
638
|
+
}
|
|
639
|
+
.video-overlay-bottom .incall-btn {
|
|
640
|
+
backdrop-filter: blur(8px);
|
|
641
|
+
}
|
|
642
|
+
.video-btn-mute {
|
|
643
|
+
background: rgba(255,255,255,0.2) !important;
|
|
644
|
+
color: #fff !important;
|
|
645
|
+
border: none !important;
|
|
646
|
+
}
|
|
647
|
+
.video-btn-mute.muted {
|
|
648
|
+
background: var(--danger) !important;
|
|
649
|
+
}
|
|
650
|
+
.video-btn-camera {
|
|
651
|
+
background: rgba(255,255,255,0.2) !important;
|
|
652
|
+
color: #fff !important;
|
|
653
|
+
border: none !important;
|
|
654
|
+
}
|
|
655
|
+
.video-btn-camera.muted {
|
|
656
|
+
background: rgba(255,255,255,0.1) !important;
|
|
657
|
+
color: rgba(255,255,255,0.5) !important;
|
|
658
|
+
}
|
|
659
|
+
.video-btn-end {
|
|
660
|
+
background: var(--danger) !important;
|
|
661
|
+
color: #fff !important;
|
|
662
|
+
}
|
|
663
|
+
.video-btn-end svg { width: 28px; height: 28px; }
|
|
664
|
+
|
|
665
|
+
/* ── Contacts view ── */
|
|
666
|
+
.contacts-wrapper {
|
|
667
|
+
display: flex;
|
|
668
|
+
flex-direction: column;
|
|
669
|
+
height: 400px;
|
|
670
|
+
}
|
|
671
|
+
.contacts-search-row {
|
|
672
|
+
display: flex;
|
|
673
|
+
align-items: center;
|
|
674
|
+
gap: 8px;
|
|
675
|
+
padding: 10px 12px;
|
|
676
|
+
border-bottom: 1px solid ${f};
|
|
677
|
+
flex-shrink: 0;
|
|
678
|
+
}
|
|
679
|
+
.contacts-search-input-wrap {
|
|
680
|
+
flex: 1;
|
|
681
|
+
display: flex;
|
|
682
|
+
align-items: center;
|
|
683
|
+
gap: 8px;
|
|
684
|
+
background: var(--bg);
|
|
685
|
+
border-radius: 20px;
|
|
686
|
+
padding: 0 12px;
|
|
687
|
+
height: 36px;
|
|
688
|
+
}
|
|
689
|
+
.contacts-search-input-wrap svg {
|
|
690
|
+
width: 16px;
|
|
691
|
+
height: 16px;
|
|
692
|
+
color: var(--text-sec);
|
|
693
|
+
flex-shrink: 0;
|
|
694
|
+
}
|
|
695
|
+
.contacts-search-input {
|
|
696
|
+
flex: 1;
|
|
697
|
+
border: none;
|
|
698
|
+
background: transparent;
|
|
699
|
+
color: var(--text);
|
|
700
|
+
font-size: 13px;
|
|
701
|
+
font-family: inherit;
|
|
702
|
+
outline: none;
|
|
703
|
+
}
|
|
704
|
+
.contacts-search-input::placeholder { color: var(--text-sec); }
|
|
705
|
+
.contacts-refresh-btn {
|
|
706
|
+
width: 32px;
|
|
707
|
+
height: 32px;
|
|
708
|
+
border: none;
|
|
709
|
+
background: transparent;
|
|
710
|
+
color: var(--text-sec);
|
|
711
|
+
cursor: pointer;
|
|
712
|
+
border-radius: 50%;
|
|
713
|
+
display: flex;
|
|
714
|
+
align-items: center;
|
|
715
|
+
justify-content: center;
|
|
716
|
+
transition: background 0.2s, color 0.2s;
|
|
717
|
+
flex-shrink: 0;
|
|
718
|
+
}
|
|
719
|
+
.contacts-refresh-btn:hover { background: ${p}; color: var(--text); }
|
|
720
|
+
.contacts-refresh-btn svg { width: 18px; height: 18px; }
|
|
721
|
+
|
|
722
|
+
.contacts-list {
|
|
723
|
+
flex: 1;
|
|
724
|
+
overflow-y: auto;
|
|
725
|
+
padding: 4px 8px;
|
|
726
|
+
}
|
|
727
|
+
.contacts-loading {
|
|
728
|
+
display: flex;
|
|
729
|
+
flex-direction: column;
|
|
730
|
+
align-items: center;
|
|
731
|
+
justify-content: center;
|
|
732
|
+
padding: 40px 16px;
|
|
733
|
+
color: var(--text-sec);
|
|
734
|
+
font-size: 13px;
|
|
735
|
+
gap: 12px;
|
|
736
|
+
}
|
|
737
|
+
.spinner {
|
|
738
|
+
width: 24px;
|
|
739
|
+
height: 24px;
|
|
740
|
+
border: 3px solid ${p};
|
|
741
|
+
border-top-color: var(--primary);
|
|
742
|
+
border-radius: 50%;
|
|
743
|
+
animation: spin 0.8s linear infinite;
|
|
744
|
+
}
|
|
745
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
746
|
+
|
|
747
|
+
.contact-item {
|
|
748
|
+
display: flex;
|
|
749
|
+
align-items: center;
|
|
750
|
+
gap: 10px;
|
|
751
|
+
padding: 8px 8px;
|
|
752
|
+
border-radius: 10px;
|
|
753
|
+
cursor: default;
|
|
754
|
+
transition: background 0.15s;
|
|
755
|
+
}
|
|
756
|
+
.contact-item:hover { background: ${p}; }
|
|
757
|
+
.contact-avatar {
|
|
758
|
+
width: 36px;
|
|
759
|
+
height: 36px;
|
|
760
|
+
border-radius: 50%;
|
|
761
|
+
background: var(--bg);
|
|
762
|
+
display: flex;
|
|
763
|
+
align-items: center;
|
|
764
|
+
justify-content: center;
|
|
765
|
+
flex-shrink: 0;
|
|
766
|
+
overflow: hidden;
|
|
767
|
+
font-size: 14px;
|
|
768
|
+
font-weight: 600;
|
|
769
|
+
color: var(--text-sec);
|
|
770
|
+
}
|
|
771
|
+
.contact-avatar img {
|
|
772
|
+
width: 100%;
|
|
773
|
+
height: 100%;
|
|
774
|
+
object-fit: cover;
|
|
775
|
+
}
|
|
776
|
+
.contact-info {
|
|
777
|
+
flex: 1;
|
|
778
|
+
min-width: 0;
|
|
779
|
+
}
|
|
780
|
+
.contact-name {
|
|
781
|
+
font-size: 14px;
|
|
782
|
+
font-weight: 500;
|
|
783
|
+
color: var(--text);
|
|
784
|
+
white-space: nowrap;
|
|
785
|
+
overflow: hidden;
|
|
786
|
+
text-overflow: ellipsis;
|
|
787
|
+
}
|
|
788
|
+
.contact-phone {
|
|
789
|
+
font-size: 12px;
|
|
790
|
+
color: var(--text-sec);
|
|
791
|
+
white-space: nowrap;
|
|
792
|
+
overflow: hidden;
|
|
793
|
+
text-overflow: ellipsis;
|
|
794
|
+
}
|
|
795
|
+
.contact-call-btn {
|
|
796
|
+
width: 32px;
|
|
797
|
+
height: 32px;
|
|
798
|
+
border: none;
|
|
799
|
+
background: var(--primary);
|
|
800
|
+
color: #fff;
|
|
801
|
+
border-radius: 50%;
|
|
802
|
+
display: flex;
|
|
803
|
+
align-items: center;
|
|
804
|
+
justify-content: center;
|
|
805
|
+
cursor: pointer;
|
|
806
|
+
flex-shrink: 0;
|
|
807
|
+
transition: transform 0.15s;
|
|
808
|
+
}
|
|
809
|
+
.contact-call-btn:hover { transform: scale(1.1); }
|
|
810
|
+
.contact-call-btn:active { transform: scale(0.95); }
|
|
811
|
+
.contact-call-btn svg { width: 16px; height: 16px; }
|
|
812
|
+
|
|
813
|
+
/* Hide header tabs during video call */
|
|
814
|
+
.incall-view .video-container[style*="flex"] ~ .panel-header { display: none; }
|
|
815
|
+
`}const I='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M6.62 10.79a15.05 15.05 0 006.59 6.59l2.2-2.2a1 1 0 011.01-.24c1.12.37 2.33.57 3.57.57a1 1 0 011 1V20a1 1 0 01-1 1A17 17 0 013 4a1 1 0 011-1h3.5a1 1 0 011 1c0 1.25.2 2.45.57 3.57a1 1 0 01-.25 1.02l-2.2 2.2z"/></svg>',S='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 9c-1.6 0-3.15.25-4.6.72v3.1c0 .39-.23.74-.56.9-.98.49-1.87 1.12-2.66 1.85-.18.18-.43.28-.7.28-.28 0-.53-.11-.71-.29L.29 13.08a.956.956 0 010-1.36C3.34 8.75 7.46 7 12 7s8.66 1.75 11.71 4.72c.18.18.29.44.29.71 0 .28-.11.53-.29.71l-2.48 2.48c-.18.18-.43.29-.71.29-.27 0-.52-.11-.7-.28a11.27 11.27 0 00-2.67-1.85.99.99 0 01-.56-.9v-3.1C15.15 9.25 13.6 9 12 9z"/></svg>',tt='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56a.977.977 0 00-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/></svg>',E='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm-1-9c0-.55.45-1 1-1s1 .45 1 1v6c0 .55-.45 1-1 1s-1-.45-1-1V5z"/><path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/></svg>',D='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19 11h-1.7c0 .74-.16 1.43-.43 2.05l1.23 1.23c.56-.98.9-2.09.9-3.28zm-4.02.17c0-.06.02-.11.02-.17V5c0-1.66-1.34-3-3-3S9 3.34 9 5v.18l5.98 5.99zM4.27 3L3 4.27l6.01 6.01V11c0 1.66 1.33 3 2.99 3 .22 0 .44-.03.65-.08l1.66 1.66c-.71.33-1.5.52-2.31.52-2.76 0-5.3-2.1-5.3-5.1H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c.91-.13 1.77-.45 2.54-.9L19.73 21 21 19.73 4.27 3z"/></svg>',B='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>',A='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></svg>',V='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M21 6.5l-4 4V7c0-.55-.45-1-1-1H9.82L21 17.18V6.5zM3.27 2L2 3.27 4.73 6H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.21 0 .39-.08.54-.18L19.73 21 21 19.73 3.27 2z"/></svg>',et='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>',at='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.11c.36.53.9.89 1.59.89h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59 12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z"/></svg>',it='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 19c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zM6 1c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12-8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-6 8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/></svg>',st='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M20 15.5c-1.25 0-2.45-.2-3.57-.57a1.02 1.02 0 00-1.02.24l-2.2 2.2a15.045 15.045 0 01-6.59-6.59l2.2-2.21a.96.96 0 00.25-1A11.36 11.36 0 018.5 4c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.5c0-.55-.45-1-1-1zM19 12h2c0-4.97-4.03-9-9-9v2c3.87 0 7 3.13 7 7zm-4 0h2c0-2.76-2.24-5-5-5v2c1.66 0 3 1.34 3 3z"/></svg>',nt='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5s-3 1.34-3 3 1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>',ot='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M15.5 14h-.79l-.28-.27a6.5 6.5 0 001.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 00-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 005.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>',rt='<svg viewBox="0 0 24 24" fill="currentColor"><path d="M17.65 6.35A7.958 7.958 0 0012 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0112 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>';function H(c){const t=Math.floor(c/1e3),e=Math.floor(t/60),a=t%60;return`${String(e).padStart(2,"0")}:${String(a).padStart(2,"0")}`}function P(c){if(!c||c==="unknown")return c;const t=c.replace(/\D/g,"");return t.length<=4?c:t.length===13&&t.startsWith("55")?`+${t.slice(0,2)} (${t.slice(2,4)}) ${t.slice(4,9)}-${t.slice(9)}`:t.length===12&&t.startsWith("55")?`+${t.slice(0,2)} (${t.slice(2,4)}) ${t.slice(4,8)}-${t.slice(8)}`:`+${t}`}const ct=[{digit:"1",sub:""},{digit:"2",sub:"ABC"},{digit:"3",sub:"DEF"},{digit:"4",sub:"GHI"},{digit:"5",sub:"JKL"},{digit:"6",sub:"MNO"},{digit:"7",sub:"PQRS"},{digit:"8",sub:"TUV"},{digit:"9",sub:"WXYZ"},{digit:"*",sub:""},{digit:"0",sub:"+"},{digit:"#",sub:""}];class lt{constructor(t){this.open=!1,this.muted=!1,this.timers=new Map,this.timerStarts=new Map,this.currentView="dialer",this.contactsLoaded=!1,this.contactsCache=[],this.pictureCache=new Map,this.pictureRequested=new Set,this.activeCallId=null,this.cameraOn=!1,this.incallTimer=null,this.config=t,this.host=document.createElement("div"),this.host.id="zapi-call-widget",this.shadow=this.host.attachShadow({mode:"closed"});const e=document.createElement("style");e.textContent=Q(t.theme),this.shadow.appendChild(e),this.buildDOM(),document.body.appendChild(this.host)}buildDOM(){this.root=document.createElement("div"),this.root.className=`zapi-call-root ${this.config.position}`,this.panel=document.createElement("div"),this.panel.className="panel";const t=document.createElement("div");t.className="panel-header",t.innerHTML=`
|
|
816
|
+
<div class="panel-header-top">
|
|
817
|
+
<h3>Telefone</h3>
|
|
818
|
+
<button class="close-btn">${et}</button>
|
|
819
|
+
</div>
|
|
820
|
+
<div class="tabs">
|
|
821
|
+
<button class="tab active" data-tab="dialer">${it} Discador</button>
|
|
822
|
+
<button class="tab" data-tab="calls">${st} Chamadas</button>
|
|
823
|
+
<button class="tab" data-tab="contacts">${nt} Contatos</button>
|
|
824
|
+
</div>
|
|
825
|
+
`,t.querySelector(".close-btn").addEventListener("click",()=>this.togglePanel(!1)),this.tabDialer=t.querySelector('[data-tab="dialer"]'),this.tabCalls=t.querySelector('[data-tab="calls"]'),this.tabContacts=t.querySelector('[data-tab="contacts"]'),this.tabDialer.addEventListener("click",()=>this.switchView("dialer")),this.tabCalls.addEventListener("click",()=>this.switchView("calls")),this.tabContacts.addEventListener("click",()=>this.switchView("contacts")),this.panel.appendChild(t);const e=document.createElement("div");e.className="panel-body",this.dialerView=document.createElement("div"),this.dialerView.className="view active",this.buildDialerView(),e.appendChild(this.dialerView),this.callsView=document.createElement("div"),this.callsView.className="view",this.callsView.innerHTML=`
|
|
826
|
+
<div class="calls-view">
|
|
827
|
+
<div class="empty-state">
|
|
828
|
+
<div class="icon">📞</div>
|
|
829
|
+
<p>Nenhuma chamada ativa</p>
|
|
830
|
+
</div>
|
|
831
|
+
</div>
|
|
832
|
+
`,e.appendChild(this.callsView),this.contactsView=document.createElement("div"),this.contactsView.className="view",this.buildContactsView(),e.appendChild(this.contactsView),this.incallView=document.createElement("div"),this.incallView.className="view",this.buildInCallView(),e.appendChild(this.incallView),this.panel.appendChild(e),this.root.appendChild(this.panel),this.bubble=document.createElement("div"),this.bubble.className="bubble",this.bubble.innerHTML=`
|
|
833
|
+
${I}
|
|
834
|
+
<span class="badge">0</span>
|
|
835
|
+
<span class="conn-dot"></span>
|
|
836
|
+
`,this.badge=this.bubble.querySelector(".badge"),this.connDot=this.bubble.querySelector(".conn-dot"),this.bubble.addEventListener("click",()=>{this.togglePanel(),this.config.onResume()}),this.root.appendChild(this.bubble),this.shadow.appendChild(this.root)}buildDialerView(){const t=document.createElement("div");t.className="dialer";const e=document.createElement("div");e.className="dialer-display",this.dialerInput=document.createElement("input"),this.dialerInput.className="dialer-input",this.dialerInput.type="tel",this.dialerInput.placeholder="Digite o numero",this.dialerInput.addEventListener("keydown",o=>{o.key==="Enter"&&this.handleMakeCall()});const a=document.createElement("button");a.className="backspace-btn",a.innerHTML=at,a.addEventListener("click",()=>{this.dialerInput.value=this.dialerInput.value.slice(0,-1),this.dialerInput.focus()}),e.appendChild(this.dialerInput),e.appendChild(a),t.appendChild(e);const i=document.createElement("div");i.className="numpad";for(const o of ct){const r=document.createElement("button");r.className="numpad-key",r.innerHTML=`<span>${o.digit}</span>${o.sub?`<span class="sub">${o.sub}</span>`:""}`,r.addEventListener("click",()=>{this.dialerInput.value+=o.digit,this.dialerInput.focus()}),i.appendChild(r)}t.appendChild(i);const s=document.createElement("div");s.className="call-btns";const n=document.createElement("button");if(n.className="call-btn",n.innerHTML=I,n.addEventListener("click",()=>this.handleMakeCall(!1)),s.appendChild(n),this.config.showVideoDialButton){const o=document.createElement("button");o.className="call-btn video-call-btn",o.innerHTML=A,o.addEventListener("click",()=>this.handleMakeCall(!0)),s.appendChild(o)}t.appendChild(s),this.dialerView.appendChild(t)}buildContactsView(){const t=document.createElement("div");t.className="contacts-wrapper";const e=document.createElement("div");e.className="contacts-search-row",e.innerHTML=`
|
|
837
|
+
<div class="contacts-search-input-wrap">
|
|
838
|
+
${ot}
|
|
839
|
+
<input class="contacts-search-input" type="text" placeholder="Buscar contato..." />
|
|
840
|
+
</div>
|
|
841
|
+
<button class="contacts-refresh-btn">${rt}</button>
|
|
842
|
+
`;const a=e.querySelector(".contacts-search-input");a.addEventListener("input",()=>this.filterContacts(a.value)),e.querySelector(".contacts-refresh-btn").addEventListener("click",()=>{this.contactsLoaded=!1,this.loadContacts()}),t.appendChild(e);const s=document.createElement("div");s.className="contacts-list",s.innerHTML='<div class="empty-state"><p>Clique na aba para carregar</p></div>',t.appendChild(s),this.contactsView.appendChild(t)}loadContacts(){const t=this.contactsView.querySelector(".contacts-list");t.innerHTML='<div class="contacts-loading"><div class="spinner"></div><p>Carregando contatos...</p></div>',this.config.onRequestContacts()}filterContacts(t){const e=t.toLowerCase().trim(),a=this.contactsView.querySelector(".contacts-list"),i=e?this.contactsCache.filter(s=>s.name.toLowerCase().includes(e)||s.phone.includes(e)):this.contactsCache;this.renderContactsList(a,i)}renderContactsList(t,e){if(e.length===0){t.innerHTML='<div class="empty-state"><p>Nenhum contato encontrado</p></div>';return}t.innerHTML="";for(const a of e){const i=document.createElement("div");i.className="contact-item";const s=(a.short||a.name||"?").charAt(0).toUpperCase();i.innerHTML=`
|
|
843
|
+
<div class="contact-avatar">${a.imgUrl?`<img src="${a.imgUrl}" />`:`<span>${s}</span>`}</div>
|
|
844
|
+
<div class="contact-info">
|
|
845
|
+
<div class="contact-name">${a.name||a.phone}</div>
|
|
846
|
+
<div class="contact-phone">${a.phone}</div>
|
|
847
|
+
</div>
|
|
848
|
+
<button class="contact-call-btn">${I}</button>
|
|
849
|
+
`,i.querySelector(".contact-call-btn").addEventListener("click",()=>{const n=a.phone.replace(/[^0-9+]/g,"");this.config.onMakeCall(n,!1)}),t.appendChild(i)}}updateContacts(t){this.contactsCache=t,this.contactsLoaded=!0;const e=this.contactsView.querySelector(".contacts-list");e&&this.renderContactsList(e,t)}buildInCallView(){const t=document.createElement("div");t.className="incall-view",t.innerHTML=`
|
|
850
|
+
<div class="video-container" style="display:none;">
|
|
851
|
+
<canvas class="remote-video"></canvas>
|
|
852
|
+
<div class="video-overlay-top">
|
|
853
|
+
<div class="video-caller-info">
|
|
854
|
+
<div class="video-phone"></div>
|
|
855
|
+
<div class="video-timer">00:00</div>
|
|
856
|
+
</div>
|
|
857
|
+
</div>
|
|
858
|
+
<div class="video-overlay-bottom">
|
|
859
|
+
<button class="incall-btn video-btn-mute">${E}</button>
|
|
860
|
+
<button class="incall-btn video-btn-camera">${V}</button>
|
|
861
|
+
<button class="incall-btn video-btn-end">${S}</button>
|
|
862
|
+
</div>
|
|
863
|
+
</div>
|
|
864
|
+
<div class="audio-call-ui">
|
|
865
|
+
<div class="incall-avatar">${B}</div>
|
|
866
|
+
<div class="incall-phone"></div>
|
|
867
|
+
<div class="incall-state"></div>
|
|
868
|
+
<div class="incall-timer">00:00</div>
|
|
869
|
+
<div class="audio-level-wrap">
|
|
870
|
+
<div class="audio-level-bar"></div>
|
|
871
|
+
</div>
|
|
872
|
+
<div class="incall-controls">
|
|
873
|
+
<button class="incall-btn incall-btn-mute">${E}</button>
|
|
874
|
+
<button class="incall-btn incall-btn-camera">${V}</button>
|
|
875
|
+
<button class="incall-btn incall-btn-end">${S}</button>
|
|
876
|
+
</div>
|
|
877
|
+
</div>
|
|
878
|
+
`,this.videoContainer=t.querySelector(".video-container"),this.remoteVideoCanvas=t.querySelector(".remote-video"),this.incallPhoneEl=t.querySelector(".incall-phone"),this.incallStateEl=t.querySelector(".incall-state"),this.incallTimerEl=t.querySelector(".incall-timer"),this.audioLevelBar=t.querySelector(".audio-level-bar"),this.videoPhoneEl=t.querySelector(".video-phone"),this.videoTimerEl=t.querySelector(".video-timer"),this.incallMuteBtn=t.querySelector(".incall-btn-mute"),this.incallMuteBtn.addEventListener("click",()=>{this.muted=!this.muted,this.config.onMute(this.muted),this.updateMuteButtons()}),t.querySelector(".video-btn-mute").addEventListener("click",()=>{this.muted=!this.muted,this.config.onMute(this.muted),this.updateMuteButtons()}),t.querySelectorAll(".video-btn-camera, .incall-btn-camera").forEach(n=>{n.addEventListener("click",()=>{this.cameraOn=!this.cameraOn,this.config.onCamera(this.cameraOn),this.updateCameraButton()})}),t.querySelector(".incall-btn-end").addEventListener("click",()=>{this.activeCallId&&this.config.onReject(this.activeCallId)}),t.querySelector(".video-btn-end").addEventListener("click",()=>{this.activeCallId&&this.config.onReject(this.activeCallId)}),this.incallView.appendChild(t)}handleMakeCall(t=!1){const e=this.dialerInput.value.replace(/\s/g,"");e&&(this.config.onMakeCall(e,t),console.log("[ZAPICall] makeCall from widget:",e,t?"(video)":"(audio)"))}switchView(t){this.currentView=t,this.dialerView.classList.toggle("active",t==="dialer"),this.callsView.classList.toggle("active",t==="calls"),this.contactsView.classList.toggle("active",t==="contacts"),this.incallView.classList.toggle("active",t==="incall"),this.tabDialer.classList.toggle("active",t==="dialer"),this.tabCalls.classList.toggle("active",t==="calls"),this.tabContacts.classList.toggle("active",t==="contacts"),t==="contacts"&&!this.contactsLoaded&&this.loadContacts()}togglePanel(t){this.open=t!==void 0?t:!this.open,this.panel.classList.toggle("open",this.open)}updateCalls(t,e){this.connDot.classList.toggle("connected",e);const a=t.filter(n=>n.state!=="ended"),i=t.filter(n=>n.state==="ringing"||n.state==="preaccepted"||n.state==="calling"),s=t.find(n=>n.state==="active")||t.find(n=>n.state==="accepted")||t.find(n=>n.state==="calling");if(a.length>0?(this.badge.textContent=String(a.length),this.badge.classList.add("visible")):this.badge.classList.remove("visible"),this.bubble.classList.toggle("ringing",i.length>0),i.length>0&&!this.open&&this.togglePanel(!0),s){this.activeCallId=s.callId,this.incallPhoneEl.textContent=s.notify||P(s.from),this.incallStateEl.textContent=this.stateLabel(s.state),this.videoPhoneEl&&(this.videoPhoneEl.textContent=s.notify||P(s.from));const n=this.incallView.querySelector(".incall-avatar");n&&n.dataset.avatarPhone!==s.from&&(n.dataset.avatarPhone=s.from,n.innerHTML=this.renderAvatar(s.from)),s.state==="accepted"||s.state==="active"?(this.timerStarts.has(s.callId)||this.timerStarts.set(s.callId,Date.now()),this.startInCallTimer(s.callId)):this.stopInCallTimer(),this.currentView!=="incall"&&this.switchView("incall")}else this.currentView==="incall"&&(this.activeCallId=null,this.stopInCallTimer(),i.length>0?this.switchView("calls"):this.switchView("dialer"));i.length>0&&!s&&this.currentView==="dialer"&&this.switchView("calls"),this.renderCallsList(t)}renderCallsList(t){const e=this.callsView.querySelector(".calls-view");if(t.length===0){e.innerHTML=`
|
|
879
|
+
<div class="empty-state">
|
|
880
|
+
<div class="icon">📞</div>
|
|
881
|
+
<p>Nenhuma chamada ativa</p>
|
|
882
|
+
</div>
|
|
883
|
+
`,this.clearAllTimers();return}e.innerHTML="";for(const a of t)e.appendChild(this.buildCallCard(a))}buildCallCard(t){const e=document.createElement("div");e.className=`call-card${t.state==="ended"?" ended":""}`,e.dataset.callId=t.callId;const a=t.state==="ringing"||t.state==="preaccepted",i=t.state==="calling",s=t.state==="accepted"||t.state==="active";e.innerHTML=`
|
|
884
|
+
<div class="call-card-top">
|
|
885
|
+
<div class="avatar" data-avatar-phone="${t.from}">${this.renderAvatar(t.from)}</div>
|
|
886
|
+
<div class="call-info">
|
|
887
|
+
${t.notify?`<div class="call-name">${t.notify}</div>`:""}
|
|
888
|
+
<div class="call-phone">${P(t.from)}</div>
|
|
889
|
+
<div class="call-meta">
|
|
890
|
+
${t.isVideo?A:I}
|
|
891
|
+
<span>${t.isVideo?"Video":"Audio"}${t.isGroup?" (Grupo)":""}</span>
|
|
892
|
+
</div>
|
|
893
|
+
</div>
|
|
894
|
+
<span class="state-badge ${t.state}">${this.stateLabel(t.state)}</span>
|
|
895
|
+
</div>
|
|
896
|
+
${s?`<div class="timer" data-timer="${t.callId}">00:00</div>`:""}
|
|
897
|
+
${a?`
|
|
898
|
+
<div class="call-actions">
|
|
899
|
+
<button class="btn btn-accept" data-accept="${t.callId}">
|
|
900
|
+
${tt} Aceitar
|
|
901
|
+
</button>
|
|
902
|
+
<button class="btn btn-reject" data-reject="${t.callId}">
|
|
903
|
+
${S} Rejeitar
|
|
904
|
+
</button>
|
|
905
|
+
</div>
|
|
906
|
+
`:""}
|
|
907
|
+
${i||s?`
|
|
908
|
+
<div class="call-actions">
|
|
909
|
+
<button class="btn btn-reject" data-reject="${t.callId}" style="flex:1">
|
|
910
|
+
${S} Encerrar
|
|
911
|
+
</button>
|
|
912
|
+
</div>
|
|
913
|
+
`:""}
|
|
914
|
+
`;const n=e.querySelector(`[data-accept="${t.callId}"]`);return n==null||n.addEventListener("click",()=>{this.config.onAccept(t.callId),this.config.onResume()}),e.querySelectorAll(`[data-reject="${t.callId}"]`).forEach(r=>r.addEventListener("click",()=>this.config.onReject(t.callId))),s&&(this.timerStarts.has(t.callId)||this.timerStarts.set(t.callId,Date.now()),this.startTimer(t.callId,e)),t.state==="ended"&&this.stopTimer(t.callId),e}stateLabel(t){switch(t){case"ringing":return"Chamando";case"calling":return"Ligando...";case"preaccepted":return"Conectando";case"accepted":return"Aceita";case"active":return"Ativa";case"ended":return"Encerrada";default:return t}}startTimer(t,e){this.stopTimer(t);const a=this.timerStarts.get(t)||Date.now(),i=e.querySelector(`[data-timer="${t}"]`);if(!i)return;const s=()=>{i.textContent=H(Date.now()-a)};s(),this.timers.set(t,setInterval(s,1e3))}stopTimer(t){const e=this.timers.get(t);e&&(clearInterval(e),this.timers.delete(t))}startInCallTimer(t){this.stopInCallTimer();const e=this.timerStarts.get(t)||Date.now(),a=()=>{const i=H(Date.now()-e);this.incallTimerEl.textContent=i,this.videoTimerEl&&(this.videoTimerEl.textContent=i)};a(),this.incallTimer=setInterval(a,1e3)}stopInCallTimer(){this.incallTimer&&(clearInterval(this.incallTimer),this.incallTimer=null),this.incallTimerEl.textContent="00:00"}showVideo(t){if(!this.videoContainer)return;this.videoContainer.style.display=t?"flex":"none";const e=this.incallView.querySelector(".audio-call-ui");e&&(e.style.display=t?"none":""),t&&(this.videoPhoneEl.textContent=this.incallPhoneEl.textContent,this.videoTimerEl.textContent=this.incallTimerEl.textContent)}getRemoteCanvas(){return this.remoteVideoCanvas}resetCamera(){this.cameraOn=!1,this.updateCameraButton()}updateMuteButtons(){this.incallMuteBtn.classList.toggle("muted",this.muted),this.incallMuteBtn.innerHTML=this.muted?D:E;const t=this.incallView.querySelector(".video-btn-mute");t&&(t.classList.toggle("muted",this.muted),t.innerHTML=this.muted?D:E)}updateCameraButton(){this.incallView.querySelectorAll(".video-btn-camera, .incall-btn-camera").forEach(e=>{e.classList.toggle("muted",!this.cameraOn),e.innerHTML=this.cameraOn?A:V})}showMakeCallError(t){const e=t.includes("PHONE_NOT_ON_WHATSAPP")?"Número não encontrado":"Erro ao realizar chamada",a=this.dialerInput.placeholder;this.dialerInput.value="",this.dialerInput.placeholder=e,this.dialerInput.classList.add("error"),setTimeout(()=>{this.dialerInput.placeholder=a,this.dialerInput.classList.remove("error")},3e3)}updateAudioLevel(t){const e=Math.min(100,Math.max(0,t*100));this.audioLevelBar.style.width=`${e}%`}renderAvatar(t){const e=this.pictureCache.get(t);return e?`<img src="${e}" class="avatar-img" />`:(!this.pictureRequested.has(t)&&t&&(this.pictureRequested.add(t),this.config.onRequestProfilePicture(t)),B)}updateProfilePicture(t,e){if(this.pictureCache.set(t,e),!e)return;const a=`<img src="${e}" class="avatar-img" />`;this.shadow.querySelectorAll(`[data-avatar-phone="${t}"]`).forEach(i=>{i.innerHTML=a})}clearAllTimers(){for(const t of this.timers.values())clearInterval(t);this.timers.clear(),this.timerStarts.clear(),this.stopInCallTimer()}destroy(){this.clearAllTimers(),this.host.remove()}}class $ extends y{constructor(t){super(),this.widget=null,this.audioConfig={sampleRate:16e3,channels:1},this.connected=!1,this.micStarted=!1,this.cameraStarted=!1,this.destroyed=!1,this.sdkId="",this.contacts=[],this.opts=t,this.state=new X,this.audio=new R(this.audioConfig.sampleRate),this.video=new Y,this.ws=new j("https://staging-call.z-api.io",t.instanceId,t.getToken),this.ws.onJson=e=>this.handleJsonMessage(e),this.ws.onBinary=e=>this.handleBinaryMessage(e),this.ws.onConnectionChange=e=>this.handleConnectionChange(e),this.audio.onMicData=e=>{const a=new ArrayBuffer(1+e.byteLength);new Uint8Array(a)[0]=1,new Uint8Array(a,1).set(new Uint8Array(e)),this.ws.send(a)},this.audio.onAudioLevel=e=>{var a;this.emit("audio:level",e),(a=this.widget)==null||a.updateAudioLevel(e)},this.on("video:frame",e=>this.video.renderFrame(e)),this.video.onEncodedCameraFrame=(e,a,i,s,n,o)=>{const r=new ArrayBuffer(22+e.byteLength),l=new DataView(r);l.setUint8(0,3),l.setUint16(1,a,!0),l.setUint16(3,i,!0),l.setFloat64(5,s),l.setUint8(13,n?1:0),l.setFloat64(14,o),new Uint8Array(r,22).set(new Uint8Array(e)),this.ws.send(r)},this.video.onCameraFrame=(e,a,i)=>{const s=this.rgbaToNv12(new Uint8Array(e),a,i),n=new ArrayBuffer(5+s.byteLength),o=new DataView(n);o.setUint8(0,2),o.setUint16(1,a,!0),o.setUint16(3,i,!0),new Uint8Array(n,5).set(s),this.ws.send(n)},t.autoWidget!==!1&&(this.widget=new lt({position:t.position||"bottom-right",theme:t.theme||{},showVideoDialButton:t.showVideoDialButton===!0,onAccept:e=>this.accept(e),onReject:e=>this.reject(e),onMute:e=>this.mute(e),onCamera:e=>this.setCamera(e),onResume:()=>this.audio.resume(),onMakeCall:(e,a)=>this.makeCall(e,a),onRequestContacts:()=>this.requestContacts(),onRequestProfilePicture:e=>this.requestProfilePicture(e)}),this.video.setRemoteCanvas(this.widget.getRemoteCanvas())),this.ws.connect()}accept(t){console.log("[ZAPICall] >> call:accept",t),this.ws.sendJson({type:"call:accept",callId:t}),this.startMicIfNeeded()}reject(t){console.log("[ZAPICall] >> call:reject",t),this.ws.sendJson({type:"call:reject",callId:t})}mute(t){this.ws.sendJson({type:"call:mute",muted:t})}async setCamera(t){var e;this.ws.sendJson({type:"call:camera",enabled:t}),t?(await this.startCameraIfNeeded(),(e=this.widget)==null||e.showVideo(!0)):(this.video.stopCamera(),this.cameraStarted=!1)}isCameraActive(){return this.cameraStarted}makeCall(t,e=!1){var a,i;console.log("[ZAPICall] >> call:make",t,e?"(video)":"(audio)"),this.audio.resume(),this.ws.sendJson({type:"call:make",number:t,isVideo:e}),this.emit("call:make",t),(i=(a=this.opts).onMakeCall)==null||i.call(a,t)}getCalls(){return this.state.getAllCalls()}getActiveCall(){return this.state.getActiveCall()}isConnected(){return this.connected}getSdkId(){return this.sdkId}requestContacts(){this.ws.sendJson({type:"contacts:list"})}requestProfilePicture(t){this.ws.sendJson({type:"profile:picture",phone:t})}getContacts(){return this.contacts}destroy(){var t;this.destroyed||(this.destroyed=!0,this.ws.destroy(),this.audio.destroy(),this.video.destroy(),(t=this.widget)==null||t.destroy(),this.state.clear(),this.removeAllListeners())}handleJsonMessage(t){var e,a,i,s,n,o,r,l,p,u,x,d,m,f,b,C,M,w,k,L;switch(t.type){case"connected":this.sdkId=t.sdkId||"",t.audioConfig&&(t.audioConfig.sampleRate!==this.audioConfig.sampleRate&&(this.audio.destroy(),this.audio=new R(t.audioConfig.sampleRate),this.audio.onMicData=h=>{const v=new ArrayBuffer(1+h.byteLength);new Uint8Array(v)[0]=1,new Uint8Array(v,1).set(new Uint8Array(h)),this.ws.send(v)},this.audio.onAudioLevel=h=>{var v;this.emit("audio:level",h),(v=this.widget)==null||v.updateAudioLevel(h)}),this.audioConfig=t.audioConfig);break;case"call:incoming":{const h={callId:t.callId,from:t.from,notify:t.notify,isVideo:t.isVideo,isGroup:t.isGroup,state:t.state||"ringing",timestamp:t.timestamp};this.state.addCall(h),this.emit("call:incoming",h),(a=(e=this.opts).onIncomingCall)==null||a.call(e,h),(i=this.widget)==null||i.updateCalls(this.state.getAllCalls(),this.connected);break}case"call:outgoing":{const h={callId:t.callId,from:t.to,isVideo:t.isVideo,isGroup:!1,state:"calling",timestamp:Date.now()};this.state.addCall(h),this.emit("call:outgoing",h),(s=this.widget)==null||s.updateCalls(this.state.getAllCalls(),this.connected),this.startMicIfNeeded();break}case"call:state":{if(!this.state.getCall(t.callId))break;if(this.state.updateState(t.callId,t.state),this.emit("call:state",t.callId,t.state),t.state==="accepted"||t.state==="active"){this.startMicIfNeeded();const h=this.state.getCall(t.callId);h!=null&&h.isVideo?((n=this.widget)==null||n.showVideo(!0),this.startCameraIfNeeded()):(this.video.stopCamera(),this.cameraStarted=!1,(o=this.widget)==null||o.showVideo(!1))}(r=this.widget)==null||r.updateCalls(this.state.getAllCalls(),this.connected);break}case"call:terminated":{this.state.updateState(t.callId,"ended"),this.emit("call:terminated",t.callId,t.reason),(p=(l=this.opts).onCallTerminated)==null||p.call(l,t.callId),this.video.stopCamera(),this.video.clearCanvas(),this.cameraStarted=!1,(u=this.widget)==null||u.showVideo(!1),(x=this.widget)==null||x.resetCamera(),setTimeout(()=>{var h;this.state.removeCall(t.callId),(h=this.widget)==null||h.updateCalls(this.state.getAllCalls(),this.connected),this.state.hasActiveCalls()||(this.audio.stopMic(),this.micStarted=!1)},3e3),(d=this.widget)==null||d.updateCalls(this.state.getAllCalls(),this.connected);break}case"call:video-state":{this.state.updateIsVideo(t.callId,t.isVideo),this.emit("call:video-state",t.callId,t.isVideo);const h=this.state.getCall(t.callId);h&&(h.state==="accepted"||h.state==="active")&&(t.isVideo?(m=this.widget)==null||m.showVideo(!0):(this.video.stopCamera(),this.cameraStarted=!1,(f=this.widget)==null||f.showVideo(!1))),(b=this.widget)==null||b.updateCalls(this.state.getAllCalls(),this.connected);break}case"call:claimed":{this.state.removeCall(t.callId),this.emit("call:claimed",t.callId),(C=this.widget)==null||C.updateCalls(this.state.getAllCalls(),this.connected);break}case"call:dismissed":{this.state.removeCall(t.callId),this.emit("call:dismissed",t.callId),(M=this.widget)==null||M.updateCalls(this.state.getAllCalls(),this.connected);break}case"contacts:list":{this.contacts=t.contacts||[],this.emit("contacts:list",this.contacts),(w=this.widget)==null||w.updateContacts(this.contacts);break}case"profile:picture":{this.emit("profile:picture",t.phone,t.url),(k=this.widget)==null||k.updateProfilePicture(t.phone,t.url);break}case"pong":break;case"error":t.command==="call:make"&&(this.emit("call:make:error","",t.message||"UNKNOWN_ERROR"),(L=this.widget)==null||L.showMakeCallError(t.message||"UNKNOWN_ERROR")),this.emit("error",new Error(`${t.command}: ${t.message}`));break}}handleBinaryMessage(t){if(t.byteLength<2)return;const e=new DataView(t),a=e.getUint8(0);if(a===1&&t.byteLength>=16){const i=e.getUint16(1,!0),s=e.getUint16(3,!0),n=e.getUint8(5),o=e.getUint8(6),r=e.getUint8(7)!==0,l=e.getFloat64(8,!1),p=t.slice(16),u={width:i,height:s,format:n,orientation:o,isKeyFrame:r,timestamp:l,data:p};this.emit("video:frame",u);return}if(a===0&&t.byteLength%2===1){this.audio.playPcm(t.slice(1));return}this.audio.playPcm(t)}handleConnectionChange(t){var e,a,i;this.connected=t,t?this.emit("connected"):this.emit("disconnected"),(a=(e=this.opts).onConnectionChange)==null||a.call(e,t),(i=this.widget)==null||i.updateCalls(this.state.getAllCalls(),t)}async startCameraIfNeeded(){if(!(this.cameraStarted||this.destroyed)){this.cameraStarted=!0;try{const t=document.createElement("video");t.style.display="none",document.body.appendChild(t),await this.video.startCamera(t,320,240,30)}catch(t){console.warn("[ZAPICall] Camera start failed:",t),this.cameraStarted=!1}}}rgbaToNv12(t,e,a){const i=e*a,s=new Uint8Array(i+(i>>1));for(let o=0;o<a;o++)for(let r=0;r<e;r++){const l=(o*e+r)*4;s[o*e+r]=(66*t[l]+129*t[l+1]+25*t[l+2]+128>>8)+16}let n=i;for(let o=0;o<a;o+=2)for(let r=0;r<e;r+=2){const l=(o*e+r)*4;s[n++]=(-38*t[l]-74*t[l+1]+112*t[l+2]+128>>8)+128,s[n++]=(112*t[l]-94*t[l+1]-18*t[l+2]+128>>8)+128}return s}async startMicIfNeeded(){if(!(this.micStarted||this.destroyed)){this.micStarted=!0;try{await this.audio.startMic()}catch(t){this.emit("error",t instanceof Error?t:new Error(String(t)))}}}}const dt={NV12:0,I420:1,RGB24:2,RGBA:3,H264:100},ht={Unknown:0,Normal:1,Rotate90:2,Rotate180:3,Rotate270:4};function O(c){return new $(c)}const pt={init:O,Client:$};g.VideoFormat=dt,g.VideoOrientation=ht,g.ZAPICall=pt,g.ZAPICallClient=$,g.init=O,Object.defineProperty(g,Symbol.toStringTag,{value:"Module"})}));
|
|
915
|
+
//# sourceMappingURL=z-api-call.umd.js.map
|