callix-dialer-widget 2.5.0 → 2.6.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.
@@ -6,7 +6,7 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
6
6
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
7
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
8
8
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
9
- import React, { useState, useMemo, useRef, useEffect, useCallback, forwardRef, useLayoutEffect, useId, createContext, useImperativeHandle, StrictMode, useContext } from "react";
9
+ import React, { createContext, useMemo, useSyncExternalStore, useRef, useCallback, useContext, useEffect, useState, forwardRef, useLayoutEffect, useId, useImperativeHandle, StrictMode } from "react";
10
10
  import require$$0$2, { createPortal } from "react-dom";
11
11
  var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
12
12
  function getAugmentedNamespace(n) {
@@ -43725,6 +43725,790 @@ function requireClientSdkReact() {
43725
43725
  return clientSdkReact;
43726
43726
  }
43727
43727
  var clientSdkReactExports = requireClientSdkReact();
43728
+ class EngineSignal {
43729
+ constructor(value) {
43730
+ this.value = value;
43731
+ this.listeners = /* @__PURE__ */ new Set();
43732
+ }
43733
+ get() {
43734
+ return this.value;
43735
+ }
43736
+ set(value) {
43737
+ if (JSON.stringify(value) === JSON.stringify(this.value)) return;
43738
+ this.value = value;
43739
+ for (const listener of this.listeners) listener(value);
43740
+ }
43741
+ subscribe(fn) {
43742
+ this.listeners.add(fn);
43743
+ return () => this.listeners.delete(fn);
43744
+ }
43745
+ }
43746
+ const INITIAL_TRANSCRIPT = { segments: [], status: null, insight: null, interims: 0 };
43747
+ const MAX_SEGMENTS = 300;
43748
+ const RECONNECT_MIN_MS = 500;
43749
+ const RECONNECT_MAX_MS = 5e3;
43750
+ function engineWsUrl(baseUrl) {
43751
+ const url = new URL(baseUrl, typeof window !== "undefined" ? window.location.href : "http://localhost");
43752
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
43753
+ url.pathname = `${url.pathname.replace(/\/+$/, "")}/ws`;
43754
+ url.search = "";
43755
+ url.hash = "";
43756
+ return url.toString();
43757
+ }
43758
+ class EngineClient {
43759
+ constructor(options) {
43760
+ this.options = options;
43761
+ this.userSignal = new EngineSignal(void 0);
43762
+ this.listeners = /* @__PURE__ */ new Set();
43763
+ this.sessionLostListeners = /* @__PURE__ */ new Set();
43764
+ this.viewWaiters = [];
43765
+ this.ws = null;
43766
+ this.reconnectDelay = RECONNECT_MIN_MS;
43767
+ this.reconnectTimer = null;
43768
+ this.closed = false;
43769
+ this.iceServers = [{ urls: "stun:stun.l.google.com:19302" }];
43770
+ this.pc = null;
43771
+ this.connectionId = null;
43772
+ this.remoteSet = false;
43773
+ this.pendingCandidates = [];
43774
+ this.micStream = null;
43775
+ this.audioSeq = 0;
43776
+ this.unlockListener = null;
43777
+ this.agentId = options.agentId;
43778
+ this.session = options.session ?? null;
43779
+ this.log = options.log ?? (() => void 0);
43780
+ this.audio = options.audioElement !== void 0 ? options.audioElement : createAudioElement();
43781
+ this.state = {
43782
+ wsState: "idle",
43783
+ tabId: null,
43784
+ view: null,
43785
+ engaged: false,
43786
+ pcState: "idle",
43787
+ needsUnlock: false,
43788
+ micError: null,
43789
+ lastError: null,
43790
+ inputMuted: false,
43791
+ outputMuted: false,
43792
+ outputVolume: 100,
43793
+ transcript: INITIAL_TRANSCRIPT,
43794
+ sessionLost: null
43795
+ };
43796
+ }
43797
+ // ------------------------------------------------------------------ store
43798
+ getState() {
43799
+ return this.state;
43800
+ }
43801
+ subscribe(listener) {
43802
+ this.listeners.add(listener);
43803
+ return () => {
43804
+ this.listeners.delete(listener);
43805
+ };
43806
+ }
43807
+ setState(patch) {
43808
+ this.state = { ...this.state, ...patch };
43809
+ for (const listener of this.listeners) listener();
43810
+ }
43811
+ get currentCall() {
43812
+ var _a2, _b;
43813
+ return ((_b = (_a2 = this.state.view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.call) ?? null;
43814
+ }
43815
+ // ------------------------------------------------------------------ conexão
43816
+ connect() {
43817
+ if (this.closed) this.closed = false;
43818
+ if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) return;
43819
+ const Impl = this.options.WebSocketImpl ?? WebSocket;
43820
+ this.setState({ wsState: "connecting" });
43821
+ const ws = new Impl(engineWsUrl(this.options.url));
43822
+ this.ws = ws;
43823
+ ws.onopen = () => {
43824
+ if (this.ws !== ws) return;
43825
+ this.reconnectDelay = RECONNECT_MIN_MS;
43826
+ this.setState({ wsState: "open" });
43827
+ const hello = { type: "hello", role: "tab", agentId: this.agentId, token: this.options.token, label: this.options.label ?? "Discador", ...this.session ? { session: this.session } : {} };
43828
+ this.send(hello);
43829
+ this.log("conectado ao host do motor");
43830
+ };
43831
+ ws.onmessage = (event) => {
43832
+ if (this.ws !== ws) return;
43833
+ let message;
43834
+ try {
43835
+ message = JSON.parse(String(event.data));
43836
+ } catch {
43837
+ return;
43838
+ }
43839
+ this.handle(message);
43840
+ };
43841
+ ws.onclose = () => {
43842
+ if (this.ws !== ws) return;
43843
+ this.ws = null;
43844
+ const wasEngaged = this.state.engaged;
43845
+ this.setState({ wsState: "closed", tabId: null, engaged: false });
43846
+ if (wasEngaged) this.stopAudio();
43847
+ if (this.closed) return;
43848
+ this.log(`conexão com o motor caiu; nova tentativa em ${this.reconnectDelay} ms`);
43849
+ this.reconnectTimer = setTimeout(() => {
43850
+ this.reconnectTimer = null;
43851
+ this.connect();
43852
+ }, this.reconnectDelay);
43853
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, RECONNECT_MAX_MS);
43854
+ };
43855
+ ws.onerror = () => void 0;
43856
+ }
43857
+ /** fecha de vez (sem reconectar) e solta o áudio */
43858
+ close() {
43859
+ this.closed = true;
43860
+ if (this.reconnectTimer) {
43861
+ clearTimeout(this.reconnectTimer);
43862
+ this.reconnectTimer = null;
43863
+ }
43864
+ this.stopAudio();
43865
+ const ws = this.ws;
43866
+ this.ws = null;
43867
+ if (ws) {
43868
+ ws.onclose = null;
43869
+ ws.close();
43870
+ }
43871
+ for (const waiter of this.viewWaiters.splice(0)) clearTimeout(waiter.timer);
43872
+ this.setState({ wsState: "closed", tabId: null, engaged: false });
43873
+ }
43874
+ /** a view atual, ou a próxima que o host mandar (welcome/view) */
43875
+ waitForView(timeoutMs = 15e3) {
43876
+ if (this.state.view && this.state.tabId) return Promise.resolve(this.state.view);
43877
+ return new Promise((resolve, reject) => {
43878
+ const timer = setTimeout(() => {
43879
+ const index = this.viewWaiters.findIndex((w) => w.timer === timer);
43880
+ if (index >= 0) this.viewWaiters.splice(index, 1);
43881
+ reject(new Error("o motor não respondeu"));
43882
+ }, timeoutMs);
43883
+ this.viewWaiters.push({ resolve, timer });
43884
+ });
43885
+ }
43886
+ send(message) {
43887
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) this.ws.send(JSON.stringify(message));
43888
+ }
43889
+ handle(message) {
43890
+ switch (message.type) {
43891
+ case "welcome": {
43892
+ if (Array.isArray(message.iceServers) && message.iceServers.length > 0) this.iceServers = message.iceServers;
43893
+ this.setState({ tabId: message.tabId });
43894
+ this.applyView(message.view);
43895
+ this.log(`sou a aba ${message.tabId}`);
43896
+ if (this.options.autoEngage !== false && message.view.engagedTabId !== message.tabId) this.engage();
43897
+ break;
43898
+ }
43899
+ case "view":
43900
+ this.applyView(message.view);
43901
+ break;
43902
+ case "engage":
43903
+ this.setState({ engaged: message.engaged });
43904
+ if (message.engaged) void this.startAudio();
43905
+ else this.stopAudio();
43906
+ break;
43907
+ case "signal":
43908
+ void this.onSignal(message.payload);
43909
+ break;
43910
+ case "error":
43911
+ this.setState({ lastError: message.message });
43912
+ this.log(`erro do host: ${message.message}`);
43913
+ break;
43914
+ case "transcript":
43915
+ this.pushSegment(message.segment);
43916
+ break;
43917
+ case "transcript-sync":
43918
+ this.setState({ transcript: { segments: message.segments ?? [], status: message.status ?? null, insight: message.insight ?? null, interims: 0 } });
43919
+ break;
43920
+ case "insight": {
43921
+ const current = this.state.transcript.insight;
43922
+ if (!current || message.insight.revision >= current.revision || message.insight.callId !== current.callId) {
43923
+ this.setState({ transcript: { ...this.state.transcript, insight: message.insight } });
43924
+ }
43925
+ break;
43926
+ }
43927
+ case "transcript-status":
43928
+ this.onTranscriptStatus(message.status);
43929
+ break;
43930
+ }
43931
+ }
43932
+ applyView(view) {
43933
+ var _a2, _b;
43934
+ const previous = this.state.view;
43935
+ const lost = view.sessionLost ?? null;
43936
+ const newlyLost = Boolean(lost) && !(previous == null ? void 0 : previous.sessionLost);
43937
+ if (newlyLost) {
43938
+ this.session = null;
43939
+ this.log(`o Callix derrubou a sessão: ${(lost == null ? void 0 : lost.message) ?? ""}`);
43940
+ }
43941
+ const user = (_a2 = view.snapshot) == null ? void 0 : _a2.leg.user;
43942
+ this.userSignal.set(user ? { ...user, sessionId: 0 } : void 0);
43943
+ const call = (_b = view.snapshot) == null ? void 0 : _b.leg.call;
43944
+ const patch = { view, sessionLost: lost ? { originator: lost.originator, message: lost.message } : null };
43945
+ if (call && typeof call.muted === "boolean" && call.muted !== this.state.inputMuted && call.state !== "ended") patch.inputMuted = call.muted;
43946
+ this.setState(patch);
43947
+ for (const waiter of this.viewWaiters.splice(0)) {
43948
+ clearTimeout(waiter.timer);
43949
+ waiter.resolve(view);
43950
+ }
43951
+ if (newlyLost && lost) for (const listener of this.sessionLostListeners) listener(lost);
43952
+ }
43953
+ onSessionLost(listener) {
43954
+ this.sessionLostListeners.add(listener);
43955
+ return () => {
43956
+ this.sessionLostListeners.delete(listener);
43957
+ };
43958
+ }
43959
+ // ------------------------------------------------------------------ sessão e comandos
43960
+ /** modo sdk: o widget fez login no back-end e entrega a sessão ao host */
43961
+ setSession(session) {
43962
+ this.session = session;
43963
+ this.send({ type: "session", session });
43964
+ }
43965
+ engage() {
43966
+ this.send({ type: "engage" });
43967
+ }
43968
+ release() {
43969
+ this.send({ type: "release" });
43970
+ }
43971
+ command(command) {
43972
+ this.send({ type: "command", command });
43973
+ this.log(`comando: ${command.name}${command.phone ? ` ${command.phone}` : ""}`);
43974
+ }
43975
+ // ------------------------------------------------------------------ áudio
43976
+ setInputMuted(muted) {
43977
+ if (this.micStream) for (const track of this.micStream.getAudioTracks()) track.enabled = !muted;
43978
+ this.setState({ inputMuted: muted });
43979
+ if (this.currentCall) this.command({ name: muted ? "mute" : "unmute" });
43980
+ }
43981
+ setOutputMuted(muted) {
43982
+ if (this.audio) this.audio.muted = muted;
43983
+ this.setState({ outputMuted: muted });
43984
+ }
43985
+ setOutputVolume(volume) {
43986
+ const clamped = Math.max(0, Math.min(100, Math.round(volume)));
43987
+ if (this.audio) this.audio.volume = clamped / 100;
43988
+ this.setState({ outputVolume: clamped });
43989
+ }
43990
+ async setOutputDevice(deviceId) {
43991
+ const audio = this.audio;
43992
+ if (!(audio == null ? void 0 : audio.setSinkId)) throw new Error("este navegador não permite escolher a saída de som");
43993
+ await audio.setSinkId(deviceId);
43994
+ }
43995
+ /** depois de um toque do agente: tenta tocar o som do motor que o navegador segurou */
43996
+ async unlockAudio() {
43997
+ if (!this.audio || !this.audio.srcObject) return;
43998
+ try {
43999
+ await this.audio.play();
44000
+ this.setState({ needsUnlock: false });
44001
+ } catch {
44002
+ this.setState({ needsUnlock: true });
44003
+ }
44004
+ }
44005
+ async startAudio() {
44006
+ const seq = ++this.audioSeq;
44007
+ this.stopAudio();
44008
+ const mediaDevices = this.options.mediaDevices === void 0 ? typeof navigator !== "undefined" ? navigator.mediaDevices : null : this.options.mediaDevices;
44009
+ if (!(mediaDevices == null ? void 0 : mediaDevices.getUserMedia)) {
44010
+ this.setState({ micError: "Este navegador não permite usar o microfone pela página." });
44011
+ return;
44012
+ }
44013
+ let stream;
44014
+ try {
44015
+ stream = await mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } });
44016
+ } catch (error2) {
44017
+ const message = `Microfone indisponível: ${error2.message}`;
44018
+ this.setState({ micError: message });
44019
+ this.log(message);
44020
+ return;
44021
+ }
44022
+ if (seq !== this.audioSeq || !this.state.engaged) {
44023
+ for (const track of stream.getTracks()) track.stop();
44024
+ return;
44025
+ }
44026
+ this.micStream = stream;
44027
+ for (const track of stream.getAudioTracks()) track.enabled = !this.state.inputMuted;
44028
+ this.setState({ micError: null });
44029
+ const Impl = this.options.PeerConnectionImpl ?? RTCPeerConnection;
44030
+ const connection = new Impl({ iceServers: this.iceServers });
44031
+ this.pc = connection;
44032
+ const connectionId = Math.random().toString(36).slice(2, 10);
44033
+ this.connectionId = connectionId;
44034
+ this.remoteSet = false;
44035
+ this.pendingCandidates = [];
44036
+ for (const track of stream.getAudioTracks()) connection.addTrack(track, stream);
44037
+ connection.onicecandidate = (event) => {
44038
+ this.send({ type: "signal", payload: { connectionId, candidate: event.candidate ? event.candidate.toJSON() : null } });
44039
+ };
44040
+ connection.ontrack = (event) => {
44041
+ const remote = event.streams[0] ?? new MediaStream([event.track]);
44042
+ if (this.audio) {
44043
+ this.audio.srcObject = remote;
44044
+ this.audio.muted = this.state.outputMuted;
44045
+ this.audio.volume = this.state.outputVolume / 100;
44046
+ this.audio.play().then(() => this.setState({ needsUnlock: false })).catch(() => {
44047
+ this.setState({ needsUnlock: true });
44048
+ this.armUnlock();
44049
+ });
44050
+ }
44051
+ this.log("áudio do motor chegou");
44052
+ };
44053
+ connection.onconnectionstatechange = () => {
44054
+ if (this.pc !== connection) return;
44055
+ this.setState({ pcState: connection.connectionState });
44056
+ this.log(`WebRTC: ${connection.connectionState}`);
44057
+ if (connection.connectionState === "failed" && this.state.engaged) {
44058
+ this.log("conexão de áudio com o motor falhou; renegociando");
44059
+ void this.startAudio();
44060
+ }
44061
+ };
44062
+ const offer = await connection.createOffer();
44063
+ if (this.pc !== connection) return;
44064
+ await connection.setLocalDescription(offer);
44065
+ this.send({ type: "signal", payload: { connectionId, description: { type: offer.type, sdp: offer.sdp } } });
44066
+ this.setState({ pcState: connection.connectionState });
44067
+ }
44068
+ stopAudio() {
44069
+ if (this.pc) {
44070
+ this.pc.onconnectionstatechange = null;
44071
+ this.pc.onicecandidate = null;
44072
+ this.pc.ontrack = null;
44073
+ this.pc.close();
44074
+ this.pc = null;
44075
+ }
44076
+ this.connectionId = null;
44077
+ if (this.micStream) {
44078
+ for (const track of this.micStream.getTracks()) track.stop();
44079
+ this.micStream = null;
44080
+ }
44081
+ if (this.audio) this.audio.srcObject = null;
44082
+ if (this.state.pcState !== "idle") this.setState({ pcState: "idle" });
44083
+ }
44084
+ async onSignal(payload) {
44085
+ const connection = this.pc;
44086
+ if (!connection) return;
44087
+ if (payload.connectionId && payload.connectionId !== this.connectionId) return;
44088
+ try {
44089
+ if (payload.description) {
44090
+ await connection.setRemoteDescription(payload.description);
44091
+ this.remoteSet = true;
44092
+ for (const candidate of this.pendingCandidates.splice(0)) await connection.addIceCandidate(candidate).catch(() => void 0);
44093
+ }
44094
+ if (payload.candidate !== void 0) {
44095
+ const candidate = payload.candidate ?? void 0;
44096
+ if (!this.remoteSet && candidate) this.pendingCandidates.push(candidate);
44097
+ else await connection.addIceCandidate(candidate).catch(() => void 0);
44098
+ }
44099
+ } catch (error2) {
44100
+ this.log(`sinalização: ${error2.message}`);
44101
+ }
44102
+ }
44103
+ /** o próximo toque/tecla na página libera o som (política de autoplay) */
44104
+ armUnlock() {
44105
+ if (this.unlockListener || typeof document === "undefined") return;
44106
+ const listener = () => {
44107
+ document.removeEventListener("pointerdown", listener, true);
44108
+ document.removeEventListener("keydown", listener, true);
44109
+ this.unlockListener = null;
44110
+ void this.unlockAudio();
44111
+ };
44112
+ this.unlockListener = listener;
44113
+ document.addEventListener("pointerdown", listener, true);
44114
+ document.addEventListener("keydown", listener, true);
44115
+ }
44116
+ // ------------------------------------------------------------------ transcrição
44117
+ pushSegment(segment) {
44118
+ const current = this.state.transcript;
44119
+ let segments = current.segments;
44120
+ let insight = current.insight;
44121
+ let interims = current.interims;
44122
+ if (segments.length && segments[0].callId !== segment.callId) {
44123
+ segments = [];
44124
+ insight = null;
44125
+ interims = 0;
44126
+ }
44127
+ const index = segments.findIndex((s) => s.id === segment.id);
44128
+ const next = index >= 0 ? segments.map((s, i2) => i2 === index ? segment : s) : [...segments, segment];
44129
+ if (next.length > MAX_SEGMENTS) next.shift();
44130
+ if (segment.interim) interims += 1;
44131
+ this.setState({ transcript: { ...current, segments: next, insight, interims } });
44132
+ }
44133
+ onTranscriptStatus(status) {
44134
+ const current = this.state.transcript;
44135
+ if (!current.status || current.status.recordingId === status.recordingId || status.live === "running") {
44136
+ const fresh = current.status && current.status.recordingId !== status.recordingId;
44137
+ this.setState({ transcript: { ...current, status, ...fresh ? { segments: [], insight: null, interims: 0 } : {} } });
44138
+ }
44139
+ }
44140
+ }
44141
+ function createAudioElement() {
44142
+ if (typeof document === "undefined") return null;
44143
+ const audio = document.createElement("audio");
44144
+ audio.autoplay = true;
44145
+ audio.setAttribute("playsinline", "true");
44146
+ audio.style.display = "none";
44147
+ audio.dataset.callixEngineAudio = "true";
44148
+ document.body.appendChild(audio);
44149
+ return audio;
44150
+ }
44151
+ const EngineClientContext = createContext(null);
44152
+ function EngineClientProvider({ client: client2, children: children2 }) {
44153
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(EngineClientContext.Provider, { value: client2, children: children2 });
44154
+ }
44155
+ function useEngineClient() {
44156
+ return useContext(EngineClientContext);
44157
+ }
44158
+ function useEngineState(client2) {
44159
+ return useSyncExternalStore(client2.subscribe.bind(client2), client2.getState.bind(client2), client2.getState.bind(client2));
44160
+ }
44161
+ const ENGINE_BREAKS = [{ id: 0, name: "Pausa" }];
44162
+ function toCallQualification(q) {
44163
+ return { id: q.id, name: q.name, isSystemQualification: false, systemQualificationType: 0 };
44164
+ }
44165
+ function qualificationsFromView(view, kind) {
44166
+ var _a2;
44167
+ return (((_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg.qualifications) ?? []).filter((q) => q.kind === kind).map(toCallQualification);
44168
+ }
44169
+ class EngineCall {
44170
+ constructor(client2, snapshot) {
44171
+ this.client = client2;
44172
+ this.listeners = /* @__PURE__ */ new Map();
44173
+ this.snapshot = snapshot;
44174
+ }
44175
+ get id() {
44176
+ return this.snapshot.id;
44177
+ }
44178
+ get direction() {
44179
+ return this.snapshot.direction;
44180
+ }
44181
+ get data() {
44182
+ var _a2;
44183
+ return {
44184
+ destinationNumber: (_a2 = this.snapshot.contact) == null ? void 0 : _a2.phone,
44185
+ campaignContactPhoneId: this.snapshot.campaignContactPhoneId,
44186
+ answeredAt: this.snapshot.answeredAt ? new Date(this.snapshot.answeredAt) : void 0
44187
+ };
44188
+ }
44189
+ get callerNumber() {
44190
+ var _a2;
44191
+ return (_a2 = this.snapshot.contact) == null ? void 0 : _a2.phone;
44192
+ }
44193
+ get peerDisplayName() {
44194
+ var _a2;
44195
+ return (_a2 = this.snapshot.contact) == null ? void 0 : _a2.name;
44196
+ }
44197
+ get isEstablished() {
44198
+ return this.snapshot.state === "active";
44199
+ }
44200
+ get isEnded() {
44201
+ return this.snapshot.state === "ended";
44202
+ }
44203
+ /** segundos desde o atendimento (ou desde o início, enquanto toca) */
44204
+ get currentDurationSeconds() {
44205
+ const from = this.snapshot.answeredAt ?? this.snapshot.startedAt;
44206
+ return Math.max(0, Math.floor((Date.now() - from) / 1e3));
44207
+ }
44208
+ answer() {
44209
+ this.client.command({ name: "answer" });
44210
+ }
44211
+ reject() {
44212
+ this.client.command({ name: "reject" });
44213
+ }
44214
+ hangup() {
44215
+ this.client.command({ name: "hangup" });
44216
+ }
44217
+ isOnHold() {
44218
+ return { local: false, remote: false };
44219
+ }
44220
+ async hold() {
44221
+ }
44222
+ async unhold() {
44223
+ }
44224
+ isMuted() {
44225
+ return this.client.getState().inputMuted;
44226
+ }
44227
+ mute() {
44228
+ this.client.setInputMuted(true);
44229
+ }
44230
+ unmute() {
44231
+ this.client.setInputMuted(false);
44232
+ }
44233
+ sendDtmf(tones) {
44234
+ this.client.command({ name: "dtmf", tones });
44235
+ }
44236
+ on(event, listener) {
44237
+ let set = this.listeners.get(event);
44238
+ if (!set) {
44239
+ set = /* @__PURE__ */ new Set();
44240
+ this.listeners.set(event, set);
44241
+ }
44242
+ set.add(listener);
44243
+ return this;
44244
+ }
44245
+ once(event, listener) {
44246
+ const wrapped = (...args) => {
44247
+ this.off(event, wrapped);
44248
+ listener(...args);
44249
+ };
44250
+ return this.on(event, wrapped);
44251
+ }
44252
+ off(event, listener) {
44253
+ var _a2;
44254
+ (_a2 = this.listeners.get(event)) == null ? void 0 : _a2.delete(listener);
44255
+ return this;
44256
+ }
44257
+ toCallData() {
44258
+ var _a2;
44259
+ return {
44260
+ id: this.snapshot.id,
44261
+ direction: this.snapshot.direction,
44262
+ destinationNumber: ((_a2 = this.snapshot.contact) == null ? void 0 : _a2.phone) ?? "",
44263
+ answeredAt: this.snapshot.answeredAt ? new Date(this.snapshot.answeredAt).toISOString() : void 0,
44264
+ campaignContactPhoneId: this.snapshot.campaignContactPhoneId,
44265
+ isMuted: this.snapshot.muted
44266
+ };
44267
+ }
44268
+ }
44269
+ function operatorStateFromView(view, call) {
44270
+ var _a2, _b, _c;
44271
+ if (!view) return void 0;
44272
+ if (((_a2 = view.sessionLost) == null ? void 0 : _a2.originator) === "anotherDevice") return { state: "startupError", error: "userIsAlreadyConnectedOnAnotherDevice" };
44273
+ const leg = (_b = view.snapshot) == null ? void 0 : _b.leg;
44274
+ if (!leg) {
44275
+ if (view.engine === "starting") return { state: "starting" };
44276
+ if (view.engine === "running") return { state: "starting" };
44277
+ return { state: "offline" };
44278
+ }
44279
+ const asCall = call ?? void 0;
44280
+ switch (leg.status) {
44281
+ case "starting":
44282
+ return { state: "starting" };
44283
+ case "available":
44284
+ return { state: "idle" };
44285
+ case "ringing":
44286
+ return { state: "callRinging", call: asCall };
44287
+ case "dialing":
44288
+ return ((_c = leg.call) == null ? void 0 : _c.state) === "ringing" ? { state: "manualCallRinging", call: asCall } : { state: "manualCallSetup", call: asCall };
44289
+ case "inCall":
44290
+ return { state: "callInProgress", call: asCall };
44291
+ case "afterCall":
44292
+ return { state: "afterCall" };
44293
+ case "onBreak":
44294
+ return { state: "onBreak", break: ENGINE_BREAKS[0] };
44295
+ case "offline":
44296
+ return { state: "offline" };
44297
+ case "error":
44298
+ return { state: "startupError", error: "unknownError" };
44299
+ }
44300
+ }
44301
+ function callInfoFromView(view) {
44302
+ var _a2;
44303
+ const leg = (_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg;
44304
+ const call = leg == null ? void 0 : leg.call;
44305
+ if (!leg || !call) return void 0;
44306
+ const successQualifications = qualificationsFromView(view, "success");
44307
+ const discardQualifications = qualificationsFromView(view, "discard");
44308
+ const contact = call.contact;
44309
+ if (contact == null ? void 0 : contact.campaign) {
44310
+ const info = {
44311
+ campaign: {
44312
+ id: 0,
44313
+ name: contact.campaign,
44314
+ type: 0,
44315
+ requireCallComments: false,
44316
+ enableTransfer: false,
44317
+ enableScheduledCall: false,
44318
+ successQualifications,
44319
+ discardQualifications,
44320
+ previewDuration: "",
44321
+ showCampaignModelOnService: false,
44322
+ skipCallQualification: false
44323
+ },
44324
+ campaignList: { id: 0, name: "" },
44325
+ campaignContact: { id: call.campaignContactPhoneId ?? 0, label: contact.name, phoneNumber: contact.phone }
44326
+ };
44327
+ return { type: "campaign", info };
44328
+ }
44329
+ return { type: "manual", info: { successQualifications, discardQualifications } };
44330
+ }
44331
+ function useEngineCall(client2) {
44332
+ var _a2, _b;
44333
+ const state = useEngineState(client2);
44334
+ const snapshot = ((_b = (_a2 = state.view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.call) ?? null;
44335
+ const ref = useRef(null);
44336
+ if (!snapshot || snapshot.state === "ended") {
44337
+ ref.current = null;
44338
+ } else if (!ref.current || ref.current.id !== snapshot.id) {
44339
+ ref.current = new EngineCall(client2, snapshot);
44340
+ } else {
44341
+ ref.current.snapshot = snapshot;
44342
+ }
44343
+ return ref.current;
44344
+ }
44345
+ function useEngineOperatorState(client2) {
44346
+ const state = useEngineState(client2);
44347
+ const call = useEngineCall(client2);
44348
+ return useMemo(() => operatorStateFromView(state.view, call), [state.view, call]);
44349
+ }
44350
+ function useEngineCurrentCall(client2) {
44351
+ const call = useEngineCall(client2);
44352
+ return call ?? void 0;
44353
+ }
44354
+ function useEngineCurrentCallInfo(client2) {
44355
+ var _a2, _b, _c, _d, _e;
44356
+ const state = useEngineState(client2);
44357
+ const view = state.view;
44358
+ const key = JSON.stringify([(_b = (_a2 = view == null ? void 0 : view.snapshot) == null ? void 0 : _a2.leg.call) == null ? void 0 : _b.id, (_d = (_c = view == null ? void 0 : view.snapshot) == null ? void 0 : _c.leg.call) == null ? void 0 : _d.contact, (_e = view == null ? void 0 : view.snapshot) == null ? void 0 : _e.leg.qualifications]);
44359
+ return useMemo(() => callInfoFromView(view), [key]);
44360
+ }
44361
+ function useEngineQualifications(client2, kind) {
44362
+ var _a2, _b;
44363
+ const state = useEngineState(client2);
44364
+ const raw = ((_b = (_a2 = state.view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.qualifications) ?? [];
44365
+ const key = JSON.stringify(raw);
44366
+ return useMemo(() => qualificationsFromView(state.view, kind), [key, kind]);
44367
+ }
44368
+ function useEngineControls(client2) {
44369
+ return useMemo(
44370
+ () => ({
44371
+ becomeAvailable: () => {
44372
+ var _a2, _b;
44373
+ const status = (_b = (_a2 = client2.getState().view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.status;
44374
+ if (status === "onBreak") client2.command({ name: "resume" });
44375
+ },
44376
+ goOffline: () => void 0,
44377
+ enterOnBreak: () => client2.command({ name: "pause" }),
44378
+ makeManualCall: (phoneNumber) => client2.command({ name: "dial", phone: phoneNumber }),
44379
+ finishAfterCall: (data) => {
44380
+ if (typeof data.qualificationId === "number") client2.command({ name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
44381
+ else client2.command({ name: "finish" });
44382
+ }
44383
+ }),
44384
+ [client2]
44385
+ );
44386
+ }
44387
+ function useEngineCurrentCallControls(client2) {
44388
+ return useMemo(
44389
+ () => ({
44390
+ answer: () => client2.command({ name: "answer" }),
44391
+ reject: () => client2.command({ name: "reject" }),
44392
+ hangup: () => client2.command({ name: "hangup" }),
44393
+ isOnHold: () => ({ local: false, remote: false }),
44394
+ hold: async () => void 0,
44395
+ unhold: async () => void 0,
44396
+ isMuted: () => client2.getState().inputMuted,
44397
+ mute: () => client2.setInputMuted(true),
44398
+ unmute: () => client2.setInputMuted(false)
44399
+ }),
44400
+ [client2]
44401
+ );
44402
+ }
44403
+ function useEngineAudioInputMuted(client2) {
44404
+ const state = useEngineState(client2);
44405
+ const set = useCallback((muted) => client2.setInputMuted(muted), [client2]);
44406
+ return [state.inputMuted, set];
44407
+ }
44408
+ function useEngineAudioOutputMuted(client2) {
44409
+ const state = useEngineState(client2);
44410
+ const set = useCallback((muted) => client2.setOutputMuted(muted), [client2]);
44411
+ return [state.outputMuted, set];
44412
+ }
44413
+ function useEngineAudioOutputVolume(client2) {
44414
+ const state = useEngineState(client2);
44415
+ const set = useCallback((volume) => client2.setOutputVolume(volume), [client2]);
44416
+ return [state.outputVolume, set];
44417
+ }
44418
+ function useEngineEventListener(client2, event, listener) {
44419
+ const ref = useRef(listener);
44420
+ ref.current = listener;
44421
+ useEffect(() => {
44422
+ return client2.onSessionLost((lost) => {
44423
+ ref.current({ originator: lost.originator });
44424
+ });
44425
+ }, [client2, event]);
44426
+ }
44427
+ const operators = /* @__PURE__ */ new WeakMap();
44428
+ function engineOperator(client2) {
44429
+ let operator = operators.get(client2);
44430
+ if (!operator) {
44431
+ const breaks = new EngineSignal(ENGINE_BREAKS);
44432
+ operator = {
44433
+ breaks,
44434
+ user: client2.userSignal,
44435
+ audioOutput: {
44436
+ setAudioOutputDevice: (id) => void client2.setOutputDevice(id).catch(() => void 0),
44437
+ setVolume: (volume) => client2.setOutputVolume(volume),
44438
+ setMute: (muted) => client2.setOutputMuted(muted)
44439
+ },
44440
+ stop: () => void 0,
44441
+ goOffline: () => void 0,
44442
+ becomeAvailable: () => {
44443
+ var _a2, _b;
44444
+ if (((_b = (_a2 = client2.getState().view) == null ? void 0 : _a2.snapshot) == null ? void 0 : _b.leg.status) === "onBreak") client2.command({ name: "resume" });
44445
+ },
44446
+ enterOnBreak: () => client2.command({ name: "pause" }),
44447
+ makeManualCall: (phoneNumber) => client2.command({ name: "dial", phone: phoneNumber }),
44448
+ finishAfterCall: (data) => {
44449
+ if (typeof data.qualificationId === "number") client2.command({ name: "qualify", qualificationId: data.qualificationId, result: data.result === "failure" ? "discard" : "success" });
44450
+ else client2.command({ name: "finish" });
44451
+ }
44452
+ };
44453
+ operators.set(client2, operator);
44454
+ }
44455
+ return operator;
44456
+ }
44457
+ function useEngineOperator(client2) {
44458
+ return useMemo(() => engineOperator(client2), [client2]);
44459
+ }
44460
+ function useEngineTranscript(client2) {
44461
+ return useEngineState(client2).transcript;
44462
+ }
44463
+ function useCallOperator() {
44464
+ const engine = useContext(EngineClientContext);
44465
+ return engine ? useEngineOperator(engine) : clientSdkReactExports.useCallOperator();
44466
+ }
44467
+ function useCallOperatorState() {
44468
+ const engine = useContext(EngineClientContext);
44469
+ return engine ? useEngineOperatorState(engine) : clientSdkReactExports.useCallOperatorState();
44470
+ }
44471
+ function useCallOperatorControls() {
44472
+ const engine = useContext(EngineClientContext);
44473
+ return engine ? useEngineControls(engine) : clientSdkReactExports.useCallOperatorControls();
44474
+ }
44475
+ function useCallOperatorCurrentCall() {
44476
+ const engine = useContext(EngineClientContext);
44477
+ return engine ? useEngineCurrentCall(engine) : clientSdkReactExports.useCallOperatorCurrentCall();
44478
+ }
44479
+ function useCallOperatorCurrentCallInfo() {
44480
+ const engine = useContext(EngineClientContext);
44481
+ return engine ? useEngineCurrentCallInfo(engine) : clientSdkReactExports.useCallOperatorCurrentCallInfo();
44482
+ }
44483
+ function useCallOperatorSuccessQualifications() {
44484
+ const engine = useContext(EngineClientContext);
44485
+ return engine ? useEngineQualifications(engine, "success") : clientSdkReactExports.useCallOperatorSuccessQualifications();
44486
+ }
44487
+ function useCallOperatorDiscardQualifications() {
44488
+ const engine = useContext(EngineClientContext);
44489
+ return engine ? useEngineQualifications(engine, "discard") : clientSdkReactExports.useCallOperatorDiscardQualifications();
44490
+ }
44491
+ function useCallOperatorCurrentCallControls() {
44492
+ const engine = useContext(EngineClientContext);
44493
+ return engine ? useEngineCurrentCallControls(engine) : clientSdkReactExports.useCallOperatorCurrentCallControls();
44494
+ }
44495
+ function useCallOperatorAudioInputMuted() {
44496
+ const engine = useContext(EngineClientContext);
44497
+ return engine ? useEngineAudioInputMuted(engine) : clientSdkReactExports.useCallOperatorAudioInputMuted();
44498
+ }
44499
+ function useCallOperatorAudioOutputMuted() {
44500
+ const engine = useContext(EngineClientContext);
44501
+ return engine ? useEngineAudioOutputMuted(engine) : clientSdkReactExports.useCallOperatorAudioOutputMuted();
44502
+ }
44503
+ function useCallOperatorAudioOutputVolume() {
44504
+ const engine = useContext(EngineClientContext);
44505
+ return engine ? useEngineAudioOutputVolume(engine) : clientSdkReactExports.useCallOperatorAudioOutputVolume();
44506
+ }
44507
+ function useCallOperatorEventListener(event, listener) {
44508
+ const engine = useContext(EngineClientContext);
44509
+ if (engine) useEngineEventListener(engine, event, listener);
44510
+ else clientSdkReactExports.useCallOperatorEventListener(event, listener);
44511
+ }
43728
44512
  var DefaultContext = {
43729
44513
  color: void 0,
43730
44514
  size: void 0,
@@ -48532,7 +49316,13 @@ function describeCallFailure(failure) {
48532
49316
  }
48533
49317
  function pickNotice(inputs) {
48534
49318
  if (inputs.sessionDrop) return describeSessionDrop(inputs.sessionDrop);
48535
- if (inputs.operatorState === "startupError") return describeStartupError(inputs.startupError);
49319
+ if (inputs.operatorState === "startupError") {
49320
+ if (inputs.startupError === "missingAudioPermissions" && inputs.deviceIssue) {
49321
+ const action = inputs.deviceIssue.action ?? "";
49322
+ return { ...inputs.deviceIssue, action: /recarregue a página/i.test(action) ? action : `${action} Depois, recarregue a página para o discador iniciar.`.trim() };
49323
+ }
49324
+ return describeStartupError(inputs.startupError);
49325
+ }
48536
49326
  if (inputs.deviceIssue) return inputs.deviceIssue;
48537
49327
  if (inputs.operatorState === "manualCallFailed" || inputs.callFailure) return describeCallFailure(inputs.callFailure);
48538
49328
  if (inputs.operatorState === "offline") return describeOffline();
@@ -48739,8 +49529,8 @@ function formatPhoneValue(raw) {
48739
49529
  }
48740
49530
  function CallInfoDisplay() {
48741
49531
  var _a2;
48742
- const callInfo = clientSdkReactExports.useCallOperatorCurrentCallInfo();
48743
- const currentCall = clientSdkReactExports.useCallOperatorCurrentCall();
49532
+ const callInfo = useCallOperatorCurrentCallInfo();
49533
+ const currentCall = useCallOperatorCurrentCall();
48744
49534
  const campaignContactPhoneId = ((_a2 = currentCall == null ? void 0 : currentCall.data) == null ? void 0 : _a2.campaignContactPhoneId) ?? null;
48745
49535
  if ((callInfo == null ? void 0 : callInfo.type) === "campaign") {
48746
49536
  return /* @__PURE__ */ jsxRuntimeExports.jsx(CampaignCallInfoDisplay, { campaignCallInfo: callInfo.info, campaignContactPhoneId });
@@ -48886,7 +49676,7 @@ function useSignal(signal) {
48886
49676
  return value;
48887
49677
  }
48888
49678
  function useOperatorBreak() {
48889
- const { enterOnBreak, becomeAvailable } = clientSdkReactExports.useCallOperatorControls();
49679
+ const { enterOnBreak, becomeAvailable } = useCallOperatorControls();
48890
49680
  return {
48891
49681
  // Use the SDK's native method
48892
49682
  goOnBreak: enterOnBreak,
@@ -52183,19 +52973,19 @@ function AuthenticatedDialer({
52183
52973
  const isMiniMode = variant === "min";
52184
52974
  const { skipManualQualification } = useDialerConfig();
52185
52975
  console.log("[FloatingDialer/AuthenticatedDialer] 🔧 skipManualQualification:", skipManualQualification);
52186
- const operator = clientSdkReactExports.useCallOperator();
52187
- const operatorState = clientSdkReactExports.useCallOperatorState();
52188
- const currentCall = clientSdkReactExports.useCallOperatorCurrentCall();
52189
- const currentCallInfo = clientSdkReactExports.useCallOperatorCurrentCallInfo();
52190
- const rawSuccessQualifications = clientSdkReactExports.useCallOperatorSuccessQualifications();
52191
- const rawDiscardQualifications = clientSdkReactExports.useCallOperatorDiscardQualifications();
52976
+ const operator = useCallOperator();
52977
+ const operatorState = useCallOperatorState();
52978
+ const currentCall = useCallOperatorCurrentCall();
52979
+ const currentCallInfo = useCallOperatorCurrentCallInfo();
52980
+ const rawSuccessQualifications = useCallOperatorSuccessQualifications();
52981
+ const rawDiscardQualifications = useCallOperatorDiscardQualifications();
52192
52982
  const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
52193
52983
  const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
52194
- const { makeManualCall, becomeAvailable, finishAfterCall } = clientSdkReactExports.useCallOperatorControls();
52195
- const { hangup, answer, reject } = clientSdkReactExports.useCallOperatorCurrentCallControls();
52196
- const [inputMuted, setInputMuted] = clientSdkReactExports.useCallOperatorAudioInputMuted();
52197
- const [outputMuted, setOutputMuted] = clientSdkReactExports.useCallOperatorAudioOutputMuted();
52198
- const [outputVolume, setOutputVolume] = clientSdkReactExports.useCallOperatorAudioOutputVolume();
52984
+ const { makeManualCall, becomeAvailable, finishAfterCall } = useCallOperatorControls();
52985
+ const { hangup, answer, reject } = useCallOperatorCurrentCallControls();
52986
+ const [inputMuted, setInputMuted] = useCallOperatorAudioInputMuted();
52987
+ const [outputMuted, setOutputMuted] = useCallOperatorAudioOutputMuted();
52988
+ const [outputVolume, setOutputVolume] = useCallOperatorAudioOutputVolume();
52199
52989
  useEffect(() => {
52200
52990
  console.log("[FloatingDialer] Estado do operador mudou:", {
52201
52991
  state: operatorState == null ? void 0 : operatorState.state,
@@ -52228,7 +53018,7 @@ function AuthenticatedDialer({
52228
53018
  const visibleInputRef = useRef(null);
52229
53019
  const [callError, setCallError] = useState(null);
52230
53020
  const [availabilityError, setAvailabilityError] = useState(null);
52231
- clientSdkReactExports.useCallOperatorEventListener("userSessionDropped", (data) => {
53021
+ useCallOperatorEventListener("userSessionDropped", (data) => {
52232
53022
  const notice = describeSessionDrop(data == null ? void 0 : data.originator);
52233
53023
  setAvailabilityError(`${notice.title}: ${notice.detail}${notice.action ? ` ${notice.action}` : ""}`);
52234
53024
  });
@@ -56798,10 +57588,29 @@ function DebugPanel({ onClose }) {
56798
57588
  showCopySuccess && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: styles.copySuccess, children: "✅ Logs copiados para a área de transferência!" })
56799
57589
  ] });
56800
57590
  }
56801
- function DialerNoticeBanner({ notice, onDismiss, onRecheck }) {
57591
+ function DialerNoticeBanner({ notice, onDismiss, onRecheck, onHeightChange }) {
57592
+ const ref = useRef(null);
57593
+ const visible = notice !== null;
57594
+ useLayoutEffect(() => {
57595
+ const el = ref.current;
57596
+ if (!onHeightChange) return;
57597
+ if (!el) {
57598
+ onHeightChange(0);
57599
+ return;
57600
+ }
57601
+ const report = () => onHeightChange(el.offsetHeight);
57602
+ report();
57603
+ if (typeof ResizeObserver === "undefined") return;
57604
+ const observer = new ResizeObserver(report);
57605
+ observer.observe(el);
57606
+ return () => {
57607
+ observer.disconnect();
57608
+ onHeightChange(0);
57609
+ };
57610
+ }, [visible, onHeightChange]);
56802
57611
  if (!notice) return null;
56803
57612
  const dismissible = notice.source === "call" || notice.source === "session";
56804
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `dialer-notice ${notice.level}`, role: notice.level === "error" ? "alert" : "status", "data-code": notice.code, children: [
57613
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref, className: `dialer-notice ${notice.level}`, role: notice.level === "error" ? "alert" : "status", "data-code": notice.code, children: [
56805
57614
  /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: `fas ${notice.level === "error" ? "fa-circle-exclamation" : "fa-triangle-exclamation"} dialer-notice-icon`, "aria-hidden": "true" }),
56806
57615
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "dialer-notice-body", children: [
56807
57616
  /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "dialer-notice-title", children: notice.title }),
@@ -56812,9 +57621,69 @@ function DialerNoticeBanner({ notice, onDismiss, onRecheck }) {
56812
57621
  dismissible && onDismiss && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "dialer-notice-close", onClick: onDismiss, "aria-label": "Fechar aviso", children: /* @__PURE__ */ jsxRuntimeExports.jsx("i", { className: "fas fa-xmark", "aria-hidden": "true" }) })
56813
57622
  ] });
56814
57623
  }
57624
+ const EMOTION_LABEL = {
57625
+ raiva: "raiva",
57626
+ irritacao: "irritação",
57627
+ frustracao: "frustração",
57628
+ desconfianca: "desconfiança",
57629
+ impaciencia: "impaciência",
57630
+ confusao: "confusão",
57631
+ nervosismo: "nervosismo",
57632
+ surpresa: "surpresa",
57633
+ desanimo: "desânimo",
57634
+ cansaco: "cansaço",
57635
+ interesse: "interesse",
57636
+ entusiasmo: "entusiasmo",
57637
+ satisfacao: "satisfação",
57638
+ alivio: "alívio",
57639
+ neutro: "neutro"
57640
+ };
57641
+ const STANCE_LABEL = {
57642
+ aberto: "cliente aberto",
57643
+ neutro: "cliente neutro",
57644
+ resistente: "cliente resistente",
57645
+ hostil: "cliente hostil",
57646
+ indefinido: "lendo a conversa"
57647
+ };
57648
+ const SPEAKER_LABEL = { agent: "Você", customer: "Cliente", mixed: "" };
57649
+ function topEmotions(segment, max2 = 2) {
57650
+ return (segment.emotions ?? []).filter((e) => e.label !== "neutro" && e.intensity >= 0.3).slice(0, max2);
57651
+ }
57652
+ function LiveCaptionPanel({ maxLines = 4 }) {
57653
+ const client2 = useEngineClient();
57654
+ if (!client2) return null;
57655
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(LiveCaptionPanelInner, { client: client2, maxLines });
57656
+ }
57657
+ function LiveCaptionPanelInner({ client: client2, maxLines }) {
57658
+ const { segments, insight, status } = useEngineTranscript(client2);
57659
+ const listening = (status == null ? void 0 : status.live) === "running";
57660
+ if (segments.length === 0 && !insight && !listening) return null;
57661
+ const rows = segments.slice(-maxLines);
57662
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "live-captions", id: "liveCaptions", "aria-live": "polite", children: [
57663
+ insight && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `live-insight stance-${insight.customer.stance}`, id: "liveInsight", title: insight.summary, children: [
57664
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-insight-stance", children: STANCE_LABEL[insight.customer.stance] ?? insight.customer.stance }),
57665
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "live-insight-interest", children: [
57666
+ "interesse ",
57667
+ Math.round(insight.customer.interest),
57668
+ "%"
57669
+ ] }),
57670
+ insight.nextMove && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-insight-next", children: insight.nextMove })
57671
+ ] }),
57672
+ rows.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "live-caption-list", children: rows.map((segment) => /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: `live-caption ${segment.speaker}${segment.interim ? " interim" : ""}${segment.refined ? " refined" : ""}`, children: [
57673
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-caption-speaker", children: SPEAKER_LABEL[segment.speaker] }),
57674
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "live-caption-text", children: segment.text }),
57675
+ topEmotions(segment).map((emotion) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `live-caption-emotion${emotion.source === "context" ? " ctx" : ""}`, title: emotion.evidence ?? "", children: [
57676
+ EMOTION_LABEL[emotion.label] ?? emotion.label,
57677
+ " ",
57678
+ Math.round(emotion.intensity * 100),
57679
+ "%"
57680
+ ] }, emotion.label))
57681
+ ] }, segment.id)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "live-caption-waiting", children: "Ouvindo a conversa…" })
57682
+ ] });
57683
+ }
56815
57684
  function useDialerNotices(options = {}) {
56816
- const operatorState = clientSdkReactExports.useCallOperatorState();
56817
- const currentCall = clientSdkReactExports.useCallOperatorCurrentCall();
57685
+ const operatorState = useCallOperatorState();
57686
+ const currentCall = useCallOperatorCurrentCall();
56818
57687
  const [deviceIssue, setDeviceIssue] = useState(null);
56819
57688
  const [sessionDrop, setSessionDrop] = useState(null);
56820
57689
  const [callFailure, setCallFailure] = useState(null);
@@ -56844,7 +57713,7 @@ function useDialerNotices(options = {}) {
56844
57713
  (_a3 = mediaDevices == null ? void 0 : mediaDevices.removeEventListener) == null ? void 0 : _a3.call(mediaDevices, "devicechange", handler);
56845
57714
  };
56846
57715
  }, [options.checkDevices]);
56847
- clientSdkReactExports.useCallOperatorEventListener("userSessionDropped", (data) => {
57716
+ useCallOperatorEventListener("userSessionDropped", (data) => {
56848
57717
  setSessionDrop((data == null ? void 0 : data.originator) ?? "unknown");
56849
57718
  });
56850
57719
  useEffect(() => {
@@ -57737,21 +58606,21 @@ function AuthenticatedMobileDialer({
57737
58606
  const [speakerDeviceId, setSpeakerDeviceId] = useState(null);
57738
58607
  const [defaultDeviceId, setDefaultDeviceId] = useState("default");
57739
58608
  const [speakerSupported, setSpeakerSupported] = useState(null);
57740
- const operator = clientSdkReactExports.useCallOperator();
57741
- const operatorState = clientSdkReactExports.useCallOperatorState();
57742
- const currentCall = clientSdkReactExports.useCallOperatorCurrentCall();
57743
- const { hangup, answer, reject } = clientSdkReactExports.useCallOperatorCurrentCallControls();
57744
- const callInfo = clientSdkReactExports.useCallOperatorCurrentCallInfo();
57745
- const rawSuccessQualifications = clientSdkReactExports.useCallOperatorSuccessQualifications();
57746
- const rawDiscardQualifications = clientSdkReactExports.useCallOperatorDiscardQualifications();
58609
+ const operator = useCallOperator();
58610
+ const operatorState = useCallOperatorState();
58611
+ const currentCall = useCallOperatorCurrentCall();
58612
+ const { hangup, answer, reject } = useCallOperatorCurrentCallControls();
58613
+ const callInfo = useCallOperatorCurrentCallInfo();
58614
+ const rawSuccessQualifications = useCallOperatorSuccessQualifications();
58615
+ const rawDiscardQualifications = useCallOperatorDiscardQualifications();
57747
58616
  const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
57748
58617
  const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
57749
- const { makeManualCall, finishAfterCall } = clientSdkReactExports.useCallOperatorControls();
58618
+ const { makeManualCall, finishAfterCall } = useCallOperatorControls();
57750
58619
  const { goOnBreak, goOffBreak } = useOperatorBreak();
57751
58620
  const breaks = useSignal(operator == null ? void 0 : operator.breaks) || [];
57752
58621
  const userData = useSignal(operator == null ? void 0 : operator.user);
57753
58622
  const isOnBreak = (operatorState == null ? void 0 : operatorState.state) === "onBreak";
57754
- const [inputMuted, setInputMuted] = clientSdkReactExports.useCallOperatorAudioInputMuted();
58623
+ const [inputMuted, setInputMuted] = useCallOperatorAudioInputMuted();
57755
58624
  const haptic = useHaptic();
57756
58625
  const dtmf = useDTMF({ enabled: true });
57757
58626
  const keySequenceRef = useRef("");
@@ -57958,6 +58827,7 @@ function AuthenticatedMobileDialer({
57958
58827
  }, [campaignFormKey]);
57959
58828
  const callStateStr = callState;
57960
58829
  const { notice: dialerNotice, dismiss: dismissDialerNotice, recheckDevices } = useDialerNotices();
58830
+ const [noticeHeight, setNoticeHeight] = useState(0);
57961
58831
  const isError = callStateStr === "startupError" || callStateStr === "offline" || (dialerNotice == null ? void 0 : dialerNotice.source) === "device" || (dialerNotice == null ? void 0 : dialerNotice.source) === "session";
57962
58832
  const isIdle = !callStateStr || callStateStr === "starting" || callStateStr === "idle" || callStateStr === "manualCallSetup" || callStateStr === "onBreak";
57963
58833
  const isConnecting = callState === "manualCallSetup" || callState === "manualCallRinging" || callState === "callRinging";
@@ -58454,7 +59324,7 @@ function AuthenticatedMobileDialer({
58454
59324
  addLog("info", "Debug", "🔒 Painel de debug fechado");
58455
59325
  } });
58456
59326
  }
58457
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "phone-container", "data-density": density === "compact" ? "compact" : "comfortable", children: [
59327
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "phone-container", "data-density": density === "compact" ? "compact" : "comfortable", style: { "--notice-h": `${noticeHeight}px` }, children: [
58458
59328
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `screen dialer-screen ${activeScreen === "dialer" ? "active" : ""}`, id: "dialerScreen", children: [
58459
59329
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "dialer-header", children: [
58460
59330
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "status-container", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: `status-badge ${statusBadgeClass}`, id: "statusBadge", children: [
@@ -58472,7 +59342,7 @@ function AuthenticatedMobileDialer({
58472
59342
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Pausa" })
58473
59343
  ] }) })
58474
59344
  ] }),
58475
- /* @__PURE__ */ jsxRuntimeExports.jsx(DialerNoticeBanner, { notice: dialerNotice, onDismiss: dismissDialerNotice, onRecheck: () => void recheckDevices() }),
59345
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialerNoticeBanner, { notice: dialerNotice, onDismiss: dismissDialerNotice, onRecheck: () => void recheckDevices(), onHeightChange: setNoticeHeight }),
58476
59346
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "phone-number-display", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
58477
59347
  PhoneNumberInput,
58478
59348
  {
@@ -58569,6 +59439,7 @@ function AuthenticatedMobileDialer({
58569
59439
  ] })
58570
59440
  ] }) })
58571
59441
  ] }),
59442
+ /* @__PURE__ */ jsxRuntimeExports.jsx(LiveCaptionPanel, {}),
58572
59443
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "call-controls", children: [
58573
59444
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "control-row", children: [
58574
59445
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
@@ -59023,14 +59894,14 @@ function AuthenticatedMiniDialer({
59023
59894
  }) {
59024
59895
  var _a2;
59025
59896
  const { skipManualQualification } = useDialerConfig();
59026
- const operatorState = clientSdkReactExports.useCallOperatorState();
59027
- const currentCallInfo = clientSdkReactExports.useCallOperatorCurrentCallInfo();
59028
- const currentCall = clientSdkReactExports.useCallOperatorCurrentCall();
59029
- const rawSuccessQualifications = clientSdkReactExports.useCallOperatorSuccessQualifications();
59030
- const rawDiscardQualifications = clientSdkReactExports.useCallOperatorDiscardQualifications();
59897
+ const operatorState = useCallOperatorState();
59898
+ const currentCallInfo = useCallOperatorCurrentCallInfo();
59899
+ const currentCall = useCallOperatorCurrentCall();
59900
+ const rawSuccessQualifications = useCallOperatorSuccessQualifications();
59901
+ const rawDiscardQualifications = useCallOperatorDiscardQualifications();
59031
59902
  const successQualifications = useMemo(() => rawSuccessQualifications ?? [], [rawSuccessQualifications]);
59032
59903
  const discardQualifications = useMemo(() => rawDiscardQualifications ?? [], [rawDiscardQualifications]);
59033
- const { finishAfterCall } = clientSdkReactExports.useCallOperatorControls();
59904
+ const { finishAfterCall } = useCallOperatorControls();
59034
59905
  const [isClientInfoModalOpen, setIsClientInfoModalOpen] = useState(false);
59035
59906
  const [isQualificationModalOpen, setIsQualificationModalOpen] = useState(false);
59036
59907
  const [callDurationSeconds, setCallDurationSeconds] = useState(0);
@@ -59324,8 +60195,8 @@ function buildHeaders(sessionToken) {
59324
60195
  }
59325
60196
  const delay = (ms2) => new Promise((resolve) => setTimeout(resolve, ms2));
59326
60197
  function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask }) {
59327
- const operatorState = clientSdkReactExports.useCallOperatorState();
59328
- const { makeManualCall } = clientSdkReactExports.useCallOperatorControls();
60198
+ const operatorState = useCallOperatorState();
60199
+ const { makeManualCall } = useCallOperatorControls();
59329
60200
  const stateRef = useRef(operatorState == null ? void 0 : operatorState.state);
59330
60201
  stateRef.current = operatorState == null ? void 0 : operatorState.state;
59331
60202
  const makeManualCallRef = useRef(makeManualCall);
@@ -59527,7 +60398,7 @@ function ClickToCallAgent({ enabled = true, pollIntervalMs, sessionToken, onTask
59527
60398
  }, [enabled, pollIntervalMs, sessionToken]);
59528
60399
  return null;
59529
60400
  }
59530
- const WIDGET_VERSION = "2.5.0";
60401
+ const WIDGET_VERSION = "2.6.0";
59531
60402
  let currentSipWebSocket = null;
59532
60403
  function sendSipUnregisterIfPossible(ws) {
59533
60404
  if (ws.readyState !== WebSocket.OPEN) return;
@@ -59654,8 +60525,8 @@ function useDialerConfig() {
59654
60525
  }
59655
60526
  const SdkCleanupManager = forwardRef(
59656
60527
  function SdkCleanupManager2({ children: children2 }, ref) {
59657
- const operator = clientSdkReactExports.useCallOperator();
59658
- const controls = clientSdkReactExports.useCallOperatorControls();
60528
+ const operator = useCallOperator();
60529
+ const controls = useCallOperatorControls();
59659
60530
  useImperativeHandle(ref, () => ({
59660
60531
  goOffline: () => {
59661
60532
  console.log("[Callix Dialer] 📴 Chamando goOffline() para enviar SIP UNREGISTER...");
@@ -59691,6 +60562,10 @@ const SdkCleanupManager = forwardRef(
59691
60562
  );
59692
60563
  function deriveOptionsFromElement(element) {
59693
60564
  const result2 = {};
60565
+ const datasetEngineUrl = element.getAttribute("data-callix-engine-url") ?? element.dataset.callixEngineUrl;
60566
+ if (datasetEngineUrl && datasetEngineUrl.trim()) result2.engineUrl = datasetEngineUrl.trim();
60567
+ const datasetEngineToken = element.getAttribute("data-callix-engine-token") ?? element.dataset.callixEngineToken;
60568
+ if (datasetEngineToken && datasetEngineToken.trim()) result2.engineToken = datasetEngineToken.trim();
59694
60569
  const datasetMode = element.getAttribute("data-callix-mode") || element.dataset.callixMode || element.dataset.mode;
59695
60570
  const datasetUsername = element.getAttribute("data-callix-username") || element.dataset.callixUsername || element.dataset.username;
59696
60571
  const classList = Array.from(element.classList);
@@ -59782,6 +60657,12 @@ class CallixDialerWidget {
59782
60657
  if (this.options.clickToCall === void 0 && derived.clickToCall !== void 0) {
59783
60658
  this.options.clickToCall = derived.clickToCall;
59784
60659
  }
60660
+ if (!this.options.engineUrl && derived.engineUrl) {
60661
+ this.options.engineUrl = derived.engineUrl;
60662
+ }
60663
+ if (!this.options.engineToken && derived.engineToken) {
60664
+ this.options.engineToken = derived.engineToken;
60665
+ }
59785
60666
  this.root = clientExports.createRoot(this.container);
59786
60667
  this.root.render(
59787
60668
  /* @__PURE__ */ jsxRuntimeExports.jsx(StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DialerWrapper, { options: this.options, containerEl: this.container }) })
@@ -59814,6 +60695,20 @@ const getStorageKeys = (username) => ({
59814
60695
  SKIP_MANUAL_QUALIFICATION: `callix_dialer_${username}_skip_manual_qualification`
59815
60696
  });
59816
60697
  const LAST_USERNAME_KEY = "callix_dialer_last_username";
60698
+ async function fetchSkipQualification(domain, token) {
60699
+ var _a2, _b;
60700
+ try {
60701
+ const response = await fetch(`https://${domain}/sdk/v1/client/user-service/config`, {
60702
+ headers: { Authorization: `Bearer ${token}` },
60703
+ credentials: "omit"
60704
+ });
60705
+ if (!response.ok) return null;
60706
+ const data = await response.json();
60707
+ return ((_b = (_a2 = data == null ? void 0 : data.service) == null ? void 0 : _a2.outboundOptions) == null ? void 0 : _b.skipQualification) === true;
60708
+ } catch {
60709
+ return null;
60710
+ }
60711
+ }
59817
60712
  function DialerWrapper({ options, containerEl }) {
59818
60713
  const [username, setUsername] = useState(null);
59819
60714
  const [isAuthenticated, setIsAuthenticated] = useState(false);
@@ -59840,8 +60735,49 @@ function DialerWrapper({ options, containerEl }) {
59840
60735
  useEffect(() => {
59841
60736
  currentSessionRef.current.token = sessionToken;
59842
60737
  }, [sessionToken]);
60738
+ const engineMode = Boolean(options.engineUrl);
60739
+ const engineModeRef = useRef(engineMode);
60740
+ engineModeRef.current = engineMode;
60741
+ const engineRef = useRef(null);
60742
+ const ensureEngine = useCallback(
60743
+ (user) => {
60744
+ const current = engineRef.current;
60745
+ if (current && current.agentId === user) {
60746
+ current.connect();
60747
+ return current;
60748
+ }
60749
+ current == null ? void 0 : current.close();
60750
+ const client2 = new EngineClient({
60751
+ url: options.engineUrl ?? "",
60752
+ token: options.engineToken ?? "",
60753
+ agentId: user,
60754
+ label: "Discador",
60755
+ log: (message) => console.log("[Callix Dialer/motor]", message)
60756
+ });
60757
+ client2.onSessionLost((lost) => {
60758
+ const notice = describeSessionDrop(lost.originator);
60759
+ setError(`${notice.title}: ${notice.detail}${notice.action ? ` ${notice.action}` : ""}`);
60760
+ setIsAuthenticated(false);
60761
+ setSessionToken(null);
60762
+ autoLoginAttemptedRef.current = true;
60763
+ });
60764
+ client2.connect();
60765
+ engineRef.current = client2;
60766
+ return client2;
60767
+ },
60768
+ [options.engineUrl, options.engineToken]
60769
+ );
60770
+ useEffect(
60771
+ () => () => {
60772
+ var _a2;
60773
+ (_a2 = engineRef.current) == null ? void 0 : _a2.close();
60774
+ engineRef.current = null;
60775
+ },
60776
+ []
60777
+ );
59843
60778
  useEffect(() => {
59844
60779
  const handleBeforeUnload = () => {
60780
+ if (engineModeRef.current) return;
59845
60781
  const { apiUrl, token } = currentSessionRef.current;
59846
60782
  if (token) {
59847
60783
  const data = JSON.stringify({ logout: true });
@@ -59906,6 +60842,77 @@ function DialerWrapper({ options, containerEl }) {
59906
60842
  setError(null);
59907
60843
  setLoginStatus("Preparando login...");
59908
60844
  console.log("[Callix Dialer] 🚀 handleLogin iniciado para usuário:", user);
60845
+ if (options.engineUrl) {
60846
+ const engineApiUrl = options.apiUrl || "/api/user-sdk-session";
60847
+ try {
60848
+ setLoginStatus("Conectando ao motor...");
60849
+ const client2 = ensureEngine(user);
60850
+ const view = await client2.waitForView(15e3);
60851
+ const domain = options.domain || new URL(engineApiUrl, window.location.href).host;
60852
+ const engineHasSession = (view.engine === "running" || view.engine === "starting") && !view.sessionLost;
60853
+ let token = null;
60854
+ if (engineHasSession) {
60855
+ try {
60856
+ token = localStorage.getItem(getStorageKeys(user).SESSION_TOKEN);
60857
+ } catch {
60858
+ token = null;
60859
+ }
60860
+ console.log("[Callix Dialer] motor já tem a sessão do agente; entrando sem novo login");
60861
+ } else {
60862
+ setLoginStatus("Autenticando no Callix...");
60863
+ const response = await fetch(engineApiUrl, {
60864
+ method: "POST",
60865
+ headers: { "Content-Type": "application/json" },
60866
+ body: JSON.stringify({ username: user }),
60867
+ credentials: "same-origin"
60868
+ });
60869
+ if (!response.ok) {
60870
+ const errorData = await response.json().catch(() => ({ error: "Erro desconhecido" }));
60871
+ throw new Error(errorData.error || `Falha na autenticação: ${response.status}`);
60872
+ }
60873
+ const data = await response.json();
60874
+ token = data.userSessionToken;
60875
+ client2.setSession({ domain, userSessionToken: token });
60876
+ setLoginStatus("Subindo o motor...");
60877
+ }
60878
+ if (token) {
60879
+ const skip = await fetchSkipQualification(domain, token);
60880
+ if (skip !== null) {
60881
+ setSkipManualQualification(skip);
60882
+ try {
60883
+ localStorage.setItem(getStorageKeys(user).SKIP_MANUAL_QUALIFICATION, String(skip));
60884
+ } catch {
60885
+ }
60886
+ }
60887
+ }
60888
+ try {
60889
+ localStorage.setItem(LAST_USERNAME_KEY, user);
60890
+ if (token) {
60891
+ const STORAGE_KEYS = getStorageKeys(user);
60892
+ localStorage.setItem(STORAGE_KEYS.SESSION_TOKEN, token);
60893
+ localStorage.setItem(STORAGE_KEYS.DOMAIN, domain);
60894
+ localStorage.setItem(STORAGE_KEYS.TIMESTAMP, Date.now().toString());
60895
+ }
60896
+ } catch {
60897
+ }
60898
+ setSessionToken(token);
60899
+ setCallixDomain(domain);
60900
+ setUsername(user);
60901
+ setIsAuthenticated(true);
60902
+ setError(null);
60903
+ setProviderKey(Date.now());
60904
+ console.log("[Callix Dialer] Conectado ao motor como", user);
60905
+ } catch (err) {
60906
+ console.error("[Callix Dialer] Erro ao entrar pelo motor:", err);
60907
+ setError(err instanceof Error ? err.message : "Erro ao conectar ao motor");
60908
+ setIsAuthenticated(false);
60909
+ setSessionToken(null);
60910
+ } finally {
60911
+ setIsLoading(false);
60912
+ setLoginStatus(null);
60913
+ }
60914
+ return;
60915
+ }
59909
60916
  const apiUrl = options.apiUrl || "/api/user-sdk-session";
59910
60917
  let previousToken;
59911
60918
  if (typeof window !== "undefined") {
@@ -60052,7 +61059,7 @@ function DialerWrapper({ options, containerEl }) {
60052
61059
  setIsLoading(false);
60053
61060
  setLoginStatus(null);
60054
61061
  }
60055
- }, [options.apiUrl, options.domain, isAuthenticated, sessionToken]);
61062
+ }, [options.apiUrl, options.domain, options.engineUrl, isAuthenticated, sessionToken, ensureEngine]);
60056
61063
  useEffect(() => {
60057
61064
  if (lastPresetRef.current !== presetUsername) {
60058
61065
  lastPresetRef.current = presetUsername;
@@ -60076,8 +61083,11 @@ function DialerWrapper({ options, containerEl }) {
60076
61083
  void handleLogin(username);
60077
61084
  }, [autoLogin, presetUsername, username, isAuthenticated, isLoading, isRestoringSession, error2, handleLogin]);
60078
61085
  const handleLogout = useCallback(async () => {
61086
+ var _a2;
60079
61087
  const currentUsername = username;
60080
- if (cleanupRef.current) {
61088
+ if (options.engineUrl) {
61089
+ (_a2 = engineRef.current) == null ? void 0 : _a2.command({ name: "logoff" });
61090
+ } else if (cleanupRef.current) {
60081
61091
  console.log("[Callix Dialer] 📴 Logout: Chamando goOffline()...");
60082
61092
  cleanupRef.current.goOffline();
60083
61093
  await new Promise((resolve) => setTimeout(resolve, 500));
@@ -60104,7 +61114,7 @@ function DialerWrapper({ options, containerEl }) {
60104
61114
  console.warn("[Callix Dialer] Não foi possível limpar sessão:", err);
60105
61115
  }
60106
61116
  }
60107
- }, [username, presetUsername]);
61117
+ }, [username, presetUsername, options.engineUrl]);
60108
61118
  const pendingAutoLogin = autoLogin && Boolean(username) && !isAuthenticated && !error2 && !autoLoginAttemptedRef.current;
60109
61119
  const loginConfirmation = useMemo(() => {
60110
61120
  if (isAuthenticated || isLoading || isRestoringSession || isDisconnecting || !username || pendingAutoLogin) {
@@ -60143,7 +61153,7 @@ function DialerWrapper({ options, containerEl }) {
60143
61153
  }
60144
61154
  ) });
60145
61155
  }
60146
- if (!isAuthenticated || !sessionToken || !callixDomain) {
61156
+ if (!isAuthenticated || !engineMode && (!sessionToken || !callixDomain)) {
60147
61157
  return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
60148
61158
  DialerComponent,
60149
61159
  {
@@ -60163,6 +61173,38 @@ function DialerWrapper({ options, containerEl }) {
60163
61173
  }
60164
61174
  ) });
60165
61175
  }
61176
+ if (engineMode && engineRef.current) {
61177
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(EngineClientProvider, { client: engineRef.current, children: [
61178
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
61179
+ DialerComponent,
61180
+ {
61181
+ isAuthenticated,
61182
+ username,
61183
+ onLogin: handleLogin,
61184
+ onLogout: handleLogout,
61185
+ isLoadingSession: isLoading,
61186
+ sessionError: error2,
61187
+ sessionStatus: loginStatus,
61188
+ variant: uiVariant,
61189
+ portalTarget,
61190
+ density: options.density,
61191
+ mode: dialerMode
61192
+ }
61193
+ ),
61194
+ sessionToken && /* @__PURE__ */ jsxRuntimeExports.jsx(
61195
+ ClickToCallAgent,
61196
+ {
61197
+ enabled: clickToCall.enabled,
61198
+ pollIntervalMs: clickToCall.pollIntervalMs,
61199
+ sessionToken,
61200
+ onTask: clickToCall.onTask
61201
+ }
61202
+ )
61203
+ ] }, providerKey) });
61204
+ }
61205
+ if (!sessionToken || !callixDomain) {
61206
+ return null;
61207
+ }
60166
61208
  return /* @__PURE__ */ jsxRuntimeExports.jsx(DialerConfigContext.Provider, { value: { skipManualQualification }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
60167
61209
  clientSdkReactExports.CallixClientProvider,
60168
61210
  {