@getnexorai/sdk 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat.cjs +61 -26
- package/dist/chat.cjs.map +1 -1
- package/dist/chat.d.cts +2 -2
- package/dist/chat.d.ts +2 -2
- package/dist/chat.js +1 -1
- package/dist/{chunk-YDWD2Q5S.js → chunk-NBE72ISL.js} +63 -28
- package/dist/chunk-NBE72ISL.js.map +1 -0
- package/dist/{config-eTstZgSV.d.cts → config-khGNZDHI.d.cts} +8 -0
- package/dist/{config-eTstZgSV.d.ts → config-khGNZDHI.d.ts} +8 -0
- package/dist/index.cjs +67 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/nexor.iife.js +8 -8
- package/dist/nexor.iife.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-YDWD2Q5S.js.map +0 -1
package/dist/chat.cjs
CHANGED
|
@@ -15,6 +15,7 @@ function applyDefaults(o) {
|
|
|
15
15
|
position: o.position ?? "bottom-right",
|
|
16
16
|
openOnLoad: o.openOnLoad ?? false,
|
|
17
17
|
debounceMs: o.debounceMs ?? 700,
|
|
18
|
+
requestTimeoutMs: o.requestTimeoutMs ?? 6e4,
|
|
18
19
|
container: o.container,
|
|
19
20
|
systemPrompt: o.systemPrompt,
|
|
20
21
|
clientPrompt: o.clientPrompt,
|
|
@@ -700,7 +701,6 @@ function escapeHtml(s) {
|
|
|
700
701
|
}
|
|
701
702
|
|
|
702
703
|
// src/chat/index.ts
|
|
703
|
-
var TYPING_DELAY_MS = 220;
|
|
704
704
|
function initChat(client, options) {
|
|
705
705
|
ensureBrowser();
|
|
706
706
|
if (!options || !options.workflowId) {
|
|
@@ -724,6 +724,14 @@ function initChat(client, options) {
|
|
|
724
724
|
pendingLead: { ...cfg.lead ?? {} },
|
|
725
725
|
history: []
|
|
726
726
|
};
|
|
727
|
+
const send = {
|
|
728
|
+
pending: [],
|
|
729
|
+
timer: 0,
|
|
730
|
+
inFlight: false,
|
|
731
|
+
typing: null
|
|
732
|
+
};
|
|
733
|
+
const greeting = { pending: false, timer: 0 };
|
|
734
|
+
const GREETING_DELAY_MS = 2e3;
|
|
727
735
|
launcher.el.addEventListener("click", toggle);
|
|
728
736
|
panel.closeBtn.addEventListener("click", close);
|
|
729
737
|
panel.form.addEventListener("submit", onComposerSubmit);
|
|
@@ -742,7 +750,7 @@ function initChat(client, options) {
|
|
|
742
750
|
root.setAttribute("data-open", "true");
|
|
743
751
|
clearUnread();
|
|
744
752
|
if (state.history.length === 0 && cfg.greeting && state.captured) {
|
|
745
|
-
|
|
753
|
+
scheduleGreeting();
|
|
746
754
|
}
|
|
747
755
|
panel.input.focus();
|
|
748
756
|
cfg.onOpen?.();
|
|
@@ -771,15 +779,8 @@ function initChat(client, options) {
|
|
|
771
779
|
panel.captureForm?.remove();
|
|
772
780
|
syncComposerLock();
|
|
773
781
|
panel.input.focus();
|
|
774
|
-
if (cfg.greeting && state.history.length === 0)
|
|
782
|
+
if (cfg.greeting && state.history.length === 0) scheduleGreeting();
|
|
775
783
|
}
|
|
776
|
-
const send = {
|
|
777
|
-
pending: [],
|
|
778
|
-
timer: 0,
|
|
779
|
-
typingTimer: 0,
|
|
780
|
-
inFlight: false,
|
|
781
|
-
typing: null
|
|
782
|
-
};
|
|
783
784
|
function onComposerSubmit(e) {
|
|
784
785
|
e.preventDefault();
|
|
785
786
|
const text = panel.input.value.trim();
|
|
@@ -792,11 +793,43 @@ function initChat(client, options) {
|
|
|
792
793
|
panel.captureInputs.first_name?.focus();
|
|
793
794
|
return;
|
|
794
795
|
}
|
|
796
|
+
cancelGreeting();
|
|
795
797
|
appendUser(text);
|
|
796
798
|
send.pending.push(text);
|
|
797
799
|
scheduleFlush();
|
|
798
800
|
}
|
|
801
|
+
function scheduleGreeting() {
|
|
802
|
+
if (greeting.pending) return;
|
|
803
|
+
greeting.pending = true;
|
|
804
|
+
showTyping();
|
|
805
|
+
greeting.timer = window.setTimeout(() => {
|
|
806
|
+
greeting.pending = false;
|
|
807
|
+
greeting.timer = 0;
|
|
808
|
+
hideTyping();
|
|
809
|
+
appendBot(cfg.greeting);
|
|
810
|
+
}, GREETING_DELAY_MS);
|
|
811
|
+
}
|
|
812
|
+
function cancelGreeting() {
|
|
813
|
+
if (!greeting.pending) return;
|
|
814
|
+
if (greeting.timer) window.clearTimeout(greeting.timer);
|
|
815
|
+
greeting.timer = 0;
|
|
816
|
+
greeting.pending = false;
|
|
817
|
+
hideTyping();
|
|
818
|
+
}
|
|
819
|
+
function showTyping() {
|
|
820
|
+
if (send.typing) {
|
|
821
|
+
panel.body.appendChild(send.typing);
|
|
822
|
+
scrollToBottom();
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
send.typing = appendNode(buildTypingIndicator());
|
|
826
|
+
}
|
|
827
|
+
function hideTyping() {
|
|
828
|
+
send.typing?.remove();
|
|
829
|
+
send.typing = null;
|
|
830
|
+
}
|
|
799
831
|
function scheduleFlush() {
|
|
832
|
+
showTyping();
|
|
800
833
|
if (send.timer) window.clearTimeout(send.timer);
|
|
801
834
|
send.timer = window.setTimeout(() => void flush(), cfg.debounceMs);
|
|
802
835
|
}
|
|
@@ -805,24 +838,27 @@ function initChat(client, options) {
|
|
|
805
838
|
const batch = send.pending.splice(0, send.pending.length);
|
|
806
839
|
const message = batch.join("\n");
|
|
807
840
|
send.inFlight = true;
|
|
808
|
-
send.typingTimer = window.setTimeout(() => {
|
|
809
|
-
send.typing = appendNode(buildTypingIndicator());
|
|
810
|
-
}, TYPING_DELAY_MS);
|
|
811
841
|
try {
|
|
812
|
-
const res = await client.chat(
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
842
|
+
const res = await client.chat(
|
|
843
|
+
{
|
|
844
|
+
workflow_id: cfg.workflowId,
|
|
845
|
+
session_id: sessionId,
|
|
846
|
+
message,
|
|
847
|
+
system_prompt: cfg.systemPrompt,
|
|
848
|
+
client_prompt: cfg.clientPrompt,
|
|
849
|
+
lead: Object.keys(state.pendingLead).length ? state.pendingLead : void 0,
|
|
850
|
+
metadata: cfg.metadata
|
|
851
|
+
},
|
|
852
|
+
// A chat turn is NOT idempotent (re-sending re-runs the LLM and
|
|
853
|
+
// re-inserts the message). Never retry; just give it a generous timeout.
|
|
854
|
+
{ timeoutMs: cfg.requestTimeoutMs, maxRetries: 0 }
|
|
855
|
+
);
|
|
821
856
|
if (res.lead_id && !state.leadId) {
|
|
822
857
|
state.leadId = res.lead_id;
|
|
823
858
|
cfg.onLeadCaptured?.(state.leadId);
|
|
824
859
|
}
|
|
825
860
|
const reply = (res.reply ?? "").trim();
|
|
861
|
+
hideTyping();
|
|
826
862
|
if (reply) {
|
|
827
863
|
appendBot(reply);
|
|
828
864
|
} else {
|
|
@@ -830,12 +866,11 @@ function initChat(client, options) {
|
|
|
830
866
|
cfg.onError?.(new Error("Nexor SDK: empty reply from server"));
|
|
831
867
|
}
|
|
832
868
|
} catch (err) {
|
|
869
|
+
hideTyping();
|
|
833
870
|
appendSystem(cfg.errorText);
|
|
834
871
|
cfg.onError?.(err);
|
|
835
872
|
} finally {
|
|
836
|
-
|
|
837
|
-
send.typing?.remove();
|
|
838
|
-
send.typing = null;
|
|
873
|
+
hideTyping();
|
|
839
874
|
send.inFlight = false;
|
|
840
875
|
panel.input.focus();
|
|
841
876
|
if (send.pending.length) scheduleFlush();
|
|
@@ -929,7 +964,7 @@ function initChat(client, options) {
|
|
|
929
964
|
},
|
|
930
965
|
destroy: () => {
|
|
931
966
|
if (send.timer) window.clearTimeout(send.timer);
|
|
932
|
-
if (
|
|
967
|
+
if (greeting.timer) window.clearTimeout(greeting.timer);
|
|
933
968
|
window.clearInterval(refreshTimer);
|
|
934
969
|
root.remove();
|
|
935
970
|
},
|
package/dist/chat.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/chat/config.ts","../src/chat/session.ts","../src/chat/styles.ts","../src/chat/dom.ts","../src/chat/index.ts"],"names":[],"mappings":";;;AAyHO,SAAS,cAAc,CAAA,EAAiC;AAC7D,EAAA,OAAO;AAAA,IACL,YAAY,CAAA,CAAE,UAAA;AAAA,IACd,KAAA,EAAO,EAAE,KAAA,IAAS,cAAA;AAAA,IAClB,QAAA,EAAU,EAAE,QAAA,IAAY,qCAAA;AAAA,IACxB,QAAA,EAAU,EAAE,QAAA,IAAY,sCAAA;AAAA,IACxB,SAAA,EACE,EAAE,SAAA,IAAa,4DAAA;AAAA,IACjB,QACE,CAAA,CAAE,MAAA,KACD,OAAO,SAAA,KAAc,WAAA,GAAc,UAAU,QAAA,GAAW,MAAA,CAAA;AAAA,IAC3D,WAAA,EAAa,EAAE,WAAA,IAAe,SAAA;AAAA,IAC9B,eAAA,EAAiB,EAAE,eAAA,IAAmB,SAAA;AAAA,IACtC,YAAA,EAAc,EAAE,YAAA,IAAgB,IAAA;AAAA,IAChC,QAAA,EAAU,EAAE,QAAA,IAAY,cAAA;AAAA,IACxB,UAAA,EAAY,EAAE,UAAA,IAAc,KAAA;AAAA,IAC5B,UAAA,EAAY,EAAE,UAAA,IAAc,GAAA;AAAA,IAC5B,WAAW,CAAA,CAAE,SAAA;AAAA,IACb,cAAc,CAAA,CAAE,YAAA;AAAA,IAChB,cAAc,CAAA,CAAE,YAAA;AAAA,IAChB,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,OAAA,EAAS;AAAA,MACP,QAAQ,CAAA,CAAE,OAAA,EAAS,MAAA,IAAU,CAAC,cAAc,OAAO,CAAA;AAAA,MACnD,IAAA,EAAM,CAAA,CAAE,OAAA,EAAS,IAAA,IAAQ,QAAA;AAAA,MACzB,KAAA,EAAO,CAAA,CAAE,OAAA,EAAS,KAAA,IAAS,yCAAA;AAAA,MAC3B,WAAA,EAAa,CAAA,CAAE,OAAA,EAAS,WAAA,IAAe;AAAA,KACzC;AAAA,IACA,UAAU,CAAA,CAAE,QAAA;AAAA,IACZ,QAAQ,CAAA,CAAE,MAAA;AAAA,IACV,SAAS,CAAA,CAAE,OAAA;AAAA,IACX,WAAW,CAAA,CAAE,SAAA;AAAA,IACb,gBAAgB,CAAA,CAAE,cAAA;AAAA,IAClB,SAAS,CAAA,CAAE;AAAA,GACb;AACF;AAQO,SAAS,aAAa,GAAA,EAA2B;AACtD,EAAA,IAAI,GAAA,CAAI,OAAA,CAAQ,IAAA,KAAS,MAAA,EAAQ,OAAO,KAAA;AACxC,EAAA,IAAI,IAAI,IAAA,EAAM;AACZ,IAAA,MAAM,OAAO,GAAA,CAAI,IAAA;AACjB,IAAA,OAAO,GAAA,CAAI,QAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAC,IAAA,CAAK,CAAC,CAAC,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,IAAA;AACT;;;AC/JA,IAAM,WAAA,GAAc,uBAAA;AAMb,IAAM,oBAAA,GAAuB,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAMjD,SAAS,eAAA,GAA0B;AACxC,EAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,UAAA,EAAW;AACvB,IAAA,IAAI,GAAA,IAAO,GAAA,GAAM,GAAA,CAAI,EAAA,GAAK,oBAAA,EAAsB;AAC9C,MAAA,WAAA,CAAY,GAAA,CAAI,IAAI,GAAG,CAAA;AACvB,MAAA,OAAO,GAAA,CAAI,EAAA;AAAA,IACb;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,MAAM,EAAA,GAAK,CAAA,KAAA,EAAQ,SAAA,CAAU,EAAE,CAAC,CAAA,CAAA;AAChC,EAAA,IAAI;AACF,IAAA,WAAA,CAAY,IAAI,GAAG,CAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,EAAA;AACT;AAGO,SAAS,YAAA,GAAqB;AACnC,EAAA,IAAI;AACF,IAAA,MAAM,MAAM,UAAA,EAAW;AACvB,IAAA,IAAI,KAAK,WAAA,CAAY,GAAA,CAAI,EAAA,EAAI,IAAA,CAAK,KAAK,CAAA;AAAA,EACzC,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAEA,SAAS,UAAA,GAAgD;AACvD,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,WAAW,CAAA;AACnD,EAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,EAAA,IAAI;AACF,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AACxB,IAAA,IAAI,CAAA,IAAK,OAAO,CAAA,CAAE,EAAA,KAAO,YAAY,OAAO,CAAA,CAAE,EAAA,KAAO,QAAA,EAAU,OAAO,CAAA;AAAA,EACxE,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,IAAI,GAAA,CAAI,UAAA,CAAW,OAAO,CAAA,EAAG,OAAO,EAAE,EAAA,EAAI,GAAA,EAAK,EAAA,EAAI,IAAA,CAAK,GAAA,EAAI,EAAE;AAC9D,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,WAAA,CAAY,IAAY,EAAA,EAAkB;AACjD,EAAA,MAAA,CAAO,YAAA,CAAa,QAAQ,WAAA,EAAa,IAAA,CAAK,UAAU,EAAE,EAAA,EAAI,EAAA,EAAI,CAAC,CAAA;AACrE;AAMA,SAAS,UAAU,KAAA,EAAuB;AACxC,EAAA,MAAM,GAAA,GAAM,IAAI,UAAA,CAAW,KAAK,CAAA;AAChC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,eAAA,EAAiB;AAC3D,IAAA,MAAA,CAAO,gBAAgB,GAAG,CAAA;AAAA,EAC5B,CAAA,MAAO;AACL,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,GAAG,CAAA;AAAA,EACzE;AACA,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,GAAA,EAAK,CAAC,MAAM,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAE,KAAK,EAAE,CAAA;AACxE;;;ACvEO,IAAM,SAAA,GAAY,CAAC,MAAA,EAAgB,UAAA,KAA+B;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAAA,EA2BzD,MAAM,CAAA;AAAA,SAAA,EACX,UAAU,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAA,EAuBE,MAAM,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,cAAA,EA0Eb,MAAM,CAAA;AAAA,SAAA,EACX,UAAU,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAAA,EAkFL,MAAM,CAAA;AAAA,SAAA,EACX,UAAU,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAAA,EAkGH,MAAM,CAAA;AAAA,wBAAA,EACE,MAAM,CAAA;AAAA;AAAA;AAAA,cAAA,EAGhB,MAAM,CAAA;AAAA,SAAA,EACX,UAAU,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAAA,EAuBL,MAAM,CAAA;AAAA,SAAA,EACX,UAAU,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;;;AC1UrB,IAAM,UAAA,GAAa,wBAAA;AASZ,SAAS,YAAA,CAAa,QAAgB,UAAA,EAA0B;AACrE,EAAA,IAAI,QAAA,CAAS,aAAA,CAAc,CAAA,MAAA,EAAS,UAAU,GAAG,CAAA,EAAG;AACpD,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC5C,EAAA,KAAA,CAAM,YAAA,CAAa,YAAY,GAAG,CAAA;AAClC,EAAA,KAAA,CAAM,WAAA,GAAc,SAAA,CAAU,MAAA,EAAQ,UAAU,CAAA;AAChD,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,KAAK,CAAA;AACjC;AASO,SAAS,aAAA,GAA8B;AAC5C,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC1C,EAAA,EAAA,CAAG,IAAA,GAAO,QAAA;AACV,EAAA,EAAA,CAAG,SAAA,GAAY,sBAAA;AACf,EAAA,EAAA,CAAG,YAAA,CAAa,cAAc,WAAW,CAAA;AAEzC,EAAA,EAAA,CAAG,SAAA,GAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA;AAMf,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC3C,EAAA,KAAA,CAAM,SAAA,GAAY,oBAAA;AAClB,EAAA,KAAA,CAAM,MAAM,OAAA,GAAU,MAAA;AACtB,EAAA,EAAA,CAAG,YAAY,KAAK,CAAA;AACpB,EAAA,OAAO,EAAE,IAAI,KAAA,EAAM;AACrB;AAiBO,SAAS,WAAW,GAAA,EAA6B;AACtD,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACvC,EAAA,EAAA,CAAG,SAAA,GAAY,mBAAA;AACf,EAAA,EAAA,CAAG,YAAA,CAAa,QAAQ,QAAQ,CAAA;AAChC,EAAA,EAAA,CAAG,YAAA,CAAa,YAAA,EAAc,GAAA,CAAI,KAAK,CAAA;AAEvC,EAAA,MAAM,EAAE,MAAA,EAAQ,QAAA,EAAS,GAAI,YAAY,GAAG,CAAA;AAC5C,EAAA,MAAM,EAAE,IAAA,EAAM,WAAA,EAAa,aAAA,EAAc,GAAI,UAAU,GAAG,CAAA;AAC1D,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,KAAS,aAAA,EAAc;AAC5C,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,YAAA,GAAe,WAAA,EAAY,GAAI,IAAA;AAElD,EAAA,EAAA,CAAG,YAAY,MAAM,CAAA;AACrB,EAAA,EAAA,CAAG,YAAY,IAAI,CAAA;AACnB,EAAA,EAAA,CAAG,YAAY,IAAI,CAAA;AACnB,EAAA,IAAI,MAAA,EAAQ,EAAA,CAAG,WAAA,CAAY,MAAM,CAAA;AAEjC,EAAA,OAAO,EAAE,IAAI,QAAA,EAAU,IAAA,EAAM,MAAM,KAAA,EAAO,IAAA,EAAM,aAAa,aAAA,EAAc;AAC7E;AAEA,SAAS,YAAY,GAAA,EAGnB;AACA,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC3C,EAAA,MAAA,CAAO,SAAA,GAAY,oBAAA;AAKnB,EAAA,MAAM,YAAA,GAAe,IAAI,QAAA,GACrB,CAAA;AAAA,wEAAA,EACoE,UAAA,CAAW,GAAA,CAAI,QAAQ,CAAC;AAAA,WAAA,CAAA,GAE5F,EAAA;AAEJ,EAAA,MAAA,CAAO,SAAA,GAAY;AAAA;AAAA,mCAAA,EAEgB,UAAA,CAAW,GAAA,CAAI,KAAK,CAAC,CAAA;AAAA,MAAA,EAClD,YAAY;AAAA;AAAA,EAAA,CAAA;AAIlB,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAChD,EAAA,QAAA,CAAS,IAAA,GAAO,QAAA;AAChB,EAAA,QAAA,CAAS,SAAA,GAAY,mBAAA;AACrB,EAAA,QAAA,CAAS,YAAA,CAAa,cAAc,YAAY,CAAA;AAChD,EAAA,QAAA,CAAS,SAAA,GAAY;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA;AAKrB,EAAA,MAAA,CAAO,YAAY,QAAQ,CAAA;AAC3B,EAAA,OAAO,EAAE,QAAQ,QAAA,EAAS;AAC5B;AAEA,SAAS,UAAU,GAAA,EAIjB;AACA,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACzC,EAAA,IAAA,CAAK,SAAA,GAAY,kBAAA;AACjB,EAAA,MAAM,gBAAiE,EAAC;AACxE,EAAA,IAAI,WAAA,GAAsC,IAAA;AAE1C,EAAA,IAAI,YAAA,CAAa,GAAG,CAAA,EAAG;AACrB,IAAA,WAAA,GAAc,gBAAA,CAAiB,KAAK,aAAa,CAAA;AACjD,IAAA,IAAA,CAAK,YAAY,WAAW,CAAA;AAAA,EAC9B;AACA,EAAA,OAAO,EAAE,IAAA,EAAM,WAAA,EAAa,aAAA,EAAc;AAC5C;AAEA,SAAS,gBAAA,CACP,KACA,SAAA,EACiB;AACjB,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC1C,EAAA,IAAA,CAAK,SAAA,GAAY,qBAAA;AACjB,EAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAElB,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,GAAG,CAAA;AACxC,EAAA,KAAA,CAAM,SAAA,GAAY,2BAAA;AAClB,EAAA,KAAA,CAAM,WAAA,GAAc,IAAI,OAAA,CAAQ,KAAA;AAChC,EAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AAEtB,EAAA,MAAM,MAAA,GAAS,IAAI,OAAA,CAAQ,MAAA;AAG3B,EAAA,IAAI,OAAO,QAAA,CAAS,YAAY,KAAK,MAAA,CAAO,QAAA,CAAS,WAAW,CAAA,EAAG;AACjE,IAAA,MAAM,GAAA,GAAM,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACxC,IAAA,GAAA,CAAI,SAAA,GAAY,yBAAA;AAChB,IAAA,IAAI,MAAA,CAAO,QAAA,CAAS,YAAY,CAAA,EAAG;AACjC,MAAA,SAAA,CAAU,aAAa,SAAA,CAAU,YAAA,EAAc,cAAc,MAAA,EAAQ,GAAA,CAAI,MAAM,UAAU,CAAA;AACzF,MAAA,GAAA,CAAI,WAAA,CAAY,UAAU,UAAU,CAAA;AAAA,IACtC;AACA,IAAA,IAAI,MAAA,CAAO,QAAA,CAAS,WAAW,CAAA,EAAG;AAChC,MAAA,SAAA,CAAU,YAAY,SAAA,CAAU,WAAA,EAAa,aAAa,MAAA,EAAQ,GAAA,CAAI,MAAM,SAAS,CAAA;AACrF,MAAA,GAAA,CAAI,WAAA,CAAY,UAAU,SAAS,CAAA;AAAA,IACrC;AACA,IAAA,IAAI,GAAA,CAAI,iBAAA,GAAoB,CAAA,EAAG,IAAA,CAAK,YAAY,GAAG,CAAA;AAAA,EACrD;AACA,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA,EAAG;AAC5B,IAAA,SAAA,CAAU,QAAQ,SAAA,CAAU,OAAA,EAAS,SAAS,OAAA,EAAS,GAAA,CAAI,MAAM,KAAK,CAAA;AACtE,IAAA,IAAA,CAAK,WAAA,CAAY,UAAU,KAAK,CAAA;AAAA,EAClC;AACA,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA,EAAG;AAC5B,IAAA,SAAA,CAAU,QAAQ,SAAA,CAAU,OAAA,EAAS,SAAS,KAAA,EAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AACpE,IAAA,IAAA,CAAK,WAAA,CAAY,UAAU,KAAK,CAAA;AAAA,EAClC;AAEA,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,EAAA,MAAA,CAAO,IAAA,GAAO,QAAA;AACd,EAAA,MAAA,CAAO,SAAA,GAAY,4BAAA;AACnB,EAAA,MAAA,CAAO,WAAA,GAAc,IAAI,OAAA,CAAQ,WAAA;AACjC,EAAA,IAAA,CAAK,YAAY,MAAM,CAAA;AAEvB,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,aAAA,GAIP;AACA,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC1C,EAAA,IAAA,CAAK,SAAA,GAAY,sBAAA;AAEjB,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC5C,EAAA,KAAA,CAAM,IAAA,GAAO,MAAA;AACb,EAAA,KAAA,CAAM,SAAA,GAAY,mBAAA;AAClB,EAAA,KAAA,CAAM,WAAA,GAAc,sBAAA;AACpB,EAAA,KAAA,CAAM,YAAA,GAAe,KAAA;AAErB,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC5C,EAAA,IAAA,CAAK,IAAA,GAAO,QAAA;AACZ,EAAA,IAAA,CAAK,SAAA,GAAY,kBAAA;AACjB,EAAA,IAAA,CAAK,YAAA,CAAa,cAAc,cAAc,CAAA;AAE9C,EAAA,IAAA,CAAK,SAAA,GAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAAA,CAAA;AAOjB,EAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AACtB,EAAA,IAAA,CAAK,YAAY,IAAI,CAAA;AACrB,EAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAK;AAC7B;AAEA,SAAS,WAAA,GAA8B;AACrC,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACvC,EAAA,EAAA,CAAG,SAAA,GAAY,oBAAA;AACf,EAAA,EAAA,CAAG,SAAA,GAAY,CAAA,gGAAA,CAAA;AACf,EAAA,OAAO,EAAA;AACT;AAUO,SAAS,YAAA,CACd,IAAA,EACA,IAAA,EACA,EAAA,EACA,MAAA,EACgB;AAChB,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACvC,EAAA,EAAA,CAAG,SAAA,GAAY,oCAAoC,IAAI,CAAA,CAAA;AACvD,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC1C,EAAA,IAAA,CAAK,SAAA,GAAY,sBAAA;AACjB,EAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,EAAA,EAAA,CAAG,YAAY,IAAI,CAAA;AAGnB,EAAA,IAAI,IAAA,KAAS,YAAY,EAAA,EAAI;AAC3B,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,MAAM,CAAA;AAC1C,IAAA,IAAA,CAAK,SAAA,GAAY,sBAAA;AACjB,IAAA,IAAA,CAAK,OAAA,CAAQ,EAAA,GAAK,MAAA,CAAO,EAAE,CAAA;AAC3B,IAAA,IAAA,CAAK,WAAA,GAAc,kBAAA,CAAmB,EAAA,EAAI,MAAM,CAAA;AAChD,IAAA,EAAA,CAAG,YAAY,IAAI,CAAA;AAAA,EACrB;AACA,EAAA,OAAO,EAAA;AACT;AAOO,SAAS,mBACd,EAAA,EACA,MAAA,EACA,GAAA,GAAc,IAAA,CAAK,KAAI,EACf;AACR,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,MAAM,EAAE,CAAA;AACjC,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,GAAK,CAAA;AACnC,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,IAAO,CAAA;AACpC,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,KAAQ,CAAA;AACtC,IAAA,MAAM,GAAA,GAAM,IAAI,IAAA,CAAK,kBAAA,CAAmB,QAAQ,EAAE,OAAA,EAAS,QAAQ,CAAA;AACnE,IAAA,IAAI,OAAO,IAAA,EAAO,OAAO,GAAA,CAAI,MAAA,CAAO,GAAG,QAAQ,CAAA;AAC/C,IAAA,IAAI,MAAM,EAAA,EAAI,OAAO,IAAI,MAAA,CAAO,CAAC,KAAK,QAAQ,CAAA;AAC9C,IAAA,IAAI,KAAK,EAAA,EAAI,OAAO,IAAI,MAAA,CAAO,CAAC,IAAI,MAAM,CAAA;AAC1C,IAAA,IAAI,MAAM,CAAA,EAAG,OAAO,IAAI,MAAA,CAAO,CAAC,KAAK,KAAK,CAAA;AAC1C,IAAA,OAAO,IAAI,IAAA,CAAK,EAAE,CAAA,CAAE,mBAAmB,MAAA,EAAQ;AAAA,MAC7C,GAAA,EAAK,SAAA;AAAA,MACL,KAAA,EAAO;AAAA,KACR,CAAA;AAAA,EACH,CAAA,CAAA,MAAQ;AACN,IAAA,IAAI;AACF,MAAA,OAAO,IAAI,IAAA,CAAK,EAAE,CAAA,CAAE,mBAAmB,MAAA,EAAQ;AAAA,QAC7C,IAAA,EAAM,SAAA;AAAA,QACN,MAAA,EAAQ;AAAA,OACT,CAAA;AAAA,IACH,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,EAAA;AAAA,IACT;AAAA,EACF;AACF;AAGO,SAAS,oBAAA,GAAuC;AACrD,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACvC,EAAA,EAAA,CAAG,SAAA,GAAY,oBAAA;AACf,EAAA,EAAA,CAAG,SAAA,GAAY,yCAAA;AACf,EAAA,OAAO,EAAA;AACT;AAIA,SAAS,SAAA,CACP,IAAA,EACA,WAAA,EACA,IAAA,EACA,KAAA,EACkB;AAClB,EAAA,MAAM,CAAA,GAAI,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AACxC,EAAA,CAAA,CAAE,IAAA,GAAO,IAAA;AACT,EAAA,CAAA,CAAE,IAAA,GAAO,IAAA;AACT,EAAA,CAAA,CAAE,WAAA,GAAc,WAAA;AAChB,EAAA,IAAI,KAAA,IAAS,KAAA,GAAQ,KAAA;AACrB,EAAA,OAAO,CAAA;AACT;AAOA,SAAS,WAAW,CAAA,EAAmB;AACrC,EAAA,OAAO,EACJ,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA,CACrB,OAAA,CAAQ,MAAM,MAAM,CAAA,CACpB,QAAQ,IAAA,EAAM,MAAM,EACpB,OAAA,CAAQ,IAAA,EAAM,QAAQ,CAAA,CACtB,OAAA,CAAQ,MAAM,QAAQ,CAAA;AAC3B;;;ACtSA,IAAM,eAAA,GAAkB,GAAA;AASjB,SAAS,QAAA,CACd,QACA,OAAA,EACY;AACZ,EAAA,aAAA,EAAc;AACd,EAAA,IAAI,CAAC,OAAA,IAAW,CAAC,OAAA,CAAQ,UAAA,EAAY;AACnC,IAAA,MAAM,IAAI,MAAM,8CAA8C,CAAA;AAAA,EAChE;AAEA,EAAA,MAAM,GAAA,GAAM,cAAc,OAAO,CAAA;AACjC,EAAA,YAAA,CAAa,GAAA,CAAI,WAAA,EAAa,GAAA,CAAI,eAAe,CAAA;AAEjD,EAAA,MAAM,YAAY,eAAA,EAAgB;AAClC,EAAA,MAAM,UAAA,GAAa,sBAAsB,SAAS,CAAA,CAAA;AAClD,EAAA,MAAM,IAAA,GAAO,UAAU,GAAG,CAAA;AAC1B,EAAA,MAAM,WAAW,aAAA,EAAc;AAC/B,EAAA,MAAM,KAAA,GAAQ,WAAW,GAAG,CAAA;AAC5B,EAAA,IAAA,CAAK,WAAA,CAAY,SAAS,EAAE,CAAA;AAC5B,EAAA,IAAA,CAAK,WAAA,CAAY,MAAM,EAAE,CAAA;AACzB,EAAA,CAAC,GAAA,CAAI,SAAA,IAAa,QAAA,CAAS,IAAA,EAAM,YAAY,IAAI,CAAA;AAGjD,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,MAAA,EAAQ,KAAA;AAAA,IACR,MAAA,EAAQ,CAAA;AAAA,IACR,MAAA,EAAQ,MAAA;AAAA,IACR,QAAA,EAAU,CAAC,YAAA,CAAa,GAAG,CAAA;AAAA,IAC3B,aAAa,EAAE,GAAI,GAAA,CAAI,IAAA,IAAQ,EAAC,EAAG;AAAA,IACnC,SAAS;AAAC,GACZ;AAGA,EAAA,QAAA,CAAS,EAAA,CAAG,gBAAA,CAAiB,OAAA,EAAS,MAAM,CAAA;AAC5C,EAAA,KAAA,CAAM,QAAA,CAAS,gBAAA,CAAiB,OAAA,EAAS,KAAK,CAAA;AAC9C,EAAA,KAAA,CAAM,IAAA,CAAK,gBAAA,CAAiB,QAAA,EAAU,gBAAgB,CAAA;AACtD,EAAA,KAAA,CAAM,WAAA,EAAa,gBAAA,CAAiB,QAAA,EAAU,eAAe,CAAA;AAE7D,EAAA,gBAAA,EAAiB;AAKjB,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,IAAA,KAAA,CAAM,OAAA,GAAU,QAAA;AAChB,IAAA,KAAA,MAAW,CAAA,IAAK,QAAA,EAAU,YAAA,CAAa,CAAA,CAAE,IAAA,KAAS,MAAA,GAAS,MAAA,GAAS,KAAA,EAAO,CAAA,CAAE,IAAA,EAAM,CAAA,CAAE,EAAE,CAAA;AACvF,IAAA,cAAA,EAAe;AAAA,EACjB;AAEA,EAAA,IAAI,GAAA,CAAI,YAAY,IAAA,EAAK;AAIzB,EAAA,SAAS,IAAA,GAAa;AACpB,IAAA,IAAI,MAAM,MAAA,EAAQ;AAClB,IAAA,KAAA,CAAM,MAAA,GAAS,IAAA;AACf,IAAA,IAAA,CAAK,YAAA,CAAa,aAAa,MAAM,CAAA;AACrC,IAAA,WAAA,EAAY;AAEZ,IAAA,IAAI,MAAM,OAAA,CAAQ,MAAA,KAAW,KAAK,GAAA,CAAI,QAAA,IAAY,MAAM,QAAA,EAAU;AAChE,MAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AAAA,IACxB;AACA,IAAA,KAAA,CAAM,MAAM,KAAA,EAAM;AAClB,IAAA,GAAA,CAAI,MAAA,IAAS;AAAA,EACf;AAEA,EAAA,SAAS,KAAA,GAAc;AACrB,IAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACnB,IAAA,KAAA,CAAM,MAAA,GAAS,KAAA;AACf,IAAA,IAAA,CAAK,YAAA,CAAa,aAAa,OAAO,CAAA;AACtC,IAAA,GAAA,CAAI,OAAA,IAAU;AAAA,EAChB;AAEA,EAAA,SAAS,MAAA,GAAe;AACtB,IAAA,KAAA,CAAM,MAAA,GAAS,KAAA,EAAM,GAAI,IAAA,EAAK;AAAA,EAChC;AAIA,EAAA,SAAS,gBAAgB,CAAA,EAAgB;AACvC,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,MAAM,SAAA,GAAY,kBAAkB,KAAK,CAAA;AAEzC,IAAA,MAAM,QAAA,GAAW,IAAI,OAAA,CAAQ,MAAA;AAC7B,IAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,MAAA,IAAI,CAAC,SAAA,CAAU,CAAC,CAAA,EAAG;AACjB,QAAA,KAAA,CAAM,aAAA,CAAc,CAAC,CAAA,EAAG,KAAA,EAAM;AAC9B,QAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA,KAAA,CAAM,cAAc,EAAE,GAAG,KAAA,CAAM,WAAA,EAAa,GAAG,SAAA,EAAU;AACzD,IAAA,KAAA,CAAM,QAAA,GAAW,IAAA;AACjB,IAAA,KAAA,CAAM,aAAa,MAAA,EAAO;AAC1B,IAAA,gBAAA,EAAiB;AACjB,IAAA,KAAA,CAAM,MAAM,KAAA,EAAM;AAElB,IAAA,IAAI,GAAA,CAAI,YAAY,KAAA,CAAM,OAAA,CAAQ,WAAW,CAAA,EAAG,SAAA,CAAU,IAAI,QAAQ,CAAA;AAAA,EACxE;AAWA,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,SAAS,EAAC;AAAA,IACV,KAAA,EAAO,CAAA;AAAA,IACP,WAAA,EAAa,CAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,MAAA,EAAQ;AAAA,GACV;AAEA,EAAA,SAAS,iBAAiB,CAAA,EAAgB;AACxC,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,KAAA,CAAM,IAAA,EAAK;AACpC,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,KAAA,CAAM,MAAM,KAAA,GAAQ,EAAA;AACpB,IAAA,YAAA,CAAa,IAAI,CAAA;AAAA,EACnB;AAEA,EAAA,SAAS,aAAa,IAAA,EAAoB;AACxC,IAAA,IAAI,CAAC,MAAM,QAAA,EAAU;AACnB,MAAA,KAAA,CAAM,aAAA,CAAc,YAAY,KAAA,EAAM;AACtC,MAAA;AAAA,IACF;AACA,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,IAAA,CAAK,OAAA,CAAQ,KAAK,IAAI,CAAA;AACtB,IAAA,aAAA,EAAc;AAAA,EAChB;AAEA,EAAA,SAAS,aAAA,GAAsB;AAC7B,IAAA,IAAI,IAAA,CAAK,KAAA,EAAO,MAAA,CAAO,YAAA,CAAa,KAAK,KAAK,CAAA;AAC9C,IAAA,IAAA,CAAK,KAAA,GAAQ,OAAO,UAAA,CAAW,MAAM,KAAK,KAAA,EAAM,EAAG,IAAI,UAAU,CAAA;AAAA,EACnE;AAEA,EAAA,eAAe,KAAA,GAAuB;AACpC,IAAA,IAAI,IAAA,CAAK,QAAA,IAAY,IAAA,CAAK,OAAA,CAAQ,WAAW,CAAA,EAAG;AAChD,IAAA,MAAM,QAAQ,IAAA,CAAK,OAAA,CAAQ,OAAO,CAAA,EAAG,IAAA,CAAK,QAAQ,MAAM,CAAA;AACxD,IAAA,MAAM,OAAA,GAAU,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA;AAC/B,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAGhB,IAAA,IAAA,CAAK,WAAA,GAAc,MAAA,CAAO,UAAA,CAAW,MAAM;AACzC,MAAA,IAAA,CAAK,MAAA,GAAS,UAAA,CAAW,oBAAA,EAAsB,CAAA;AAAA,IACjD,GAAG,eAAe,CAAA;AAElB,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,IAAA,CAAK;AAAA,QAC5B,aAAa,GAAA,CAAI,UAAA;AAAA,QACjB,UAAA,EAAY,SAAA;AAAA,QACZ,OAAA;AAAA,QACA,eAAe,GAAA,CAAI,YAAA;AAAA,QACnB,eAAe,GAAA,CAAI,YAAA;AAAA,QACnB,IAAA,EAAM,OAAO,IAAA,CAAK,KAAA,CAAM,WAAW,CAAA,CAAE,MAAA,GAAS,MAAM,WAAA,GAAc,KAAA,CAAA;AAAA,QAClE,UAAU,GAAA,CAAI;AAAA,OACf,CAAA;AACD,MAAA,IAAI,GAAA,CAAI,OAAA,IAAW,CAAC,KAAA,CAAM,MAAA,EAAQ;AAChC,QAAA,KAAA,CAAM,SAAS,GAAA,CAAI,OAAA;AACnB,QAAA,GAAA,CAAI,cAAA,GAAiB,MAAM,MAAM,CAAA;AAAA,MACnC;AACA,MAAA,MAAM,KAAA,GAAA,CAAS,GAAA,CAAI,KAAA,IAAS,EAAA,EAAI,IAAA,EAAK;AACrC,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,SAAA,CAAU,KAAK,CAAA;AAAA,MACjB,CAAA,MAAO;AAGL,QAAA,YAAA,CAAa,IAAI,SAAS,CAAA;AAC1B,QAAA,GAAA,CAAI,OAAA,GAAU,IAAI,KAAA,CAAM,oCAAoC,CAAC,CAAA;AAAA,MAC/D;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,YAAA,CAAa,IAAI,SAAS,CAAA;AAC1B,MAAA,GAAA,CAAI,UAAU,GAAY,CAAA;AAAA,IAC5B,CAAA,SAAE;AACA,MAAA,MAAA,CAAO,YAAA,CAAa,KAAK,WAAW,CAAA;AACpC,MAAA,IAAA,CAAK,QAAQ,MAAA,EAAO;AACpB,MAAA,IAAA,CAAK,MAAA,GAAS,IAAA;AACd,MAAA,IAAA,CAAK,QAAA,GAAW,KAAA;AAChB,MAAA,KAAA,CAAM,MAAM,KAAA,EAAM;AAElB,MAAA,IAAI,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,aAAA,EAAc;AAAA,IACzC;AAAA,EACF;AAIA,EAAA,SAAS,WAAW,IAAA,EAAoB;AACtC,IAAA,MAAM,EAAA,GAAK,KAAK,GAAA,EAAI;AACpB,IAAA,YAAA,CAAa,MAAA,EAAQ,MAAM,EAAE,CAAA;AAC7B,IAAA,KAAA,CAAM,QAAQ,IAAA,CAAK,EAAE,MAAM,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAC7C,IAAA,WAAA,EAAY;AACZ,IAAA,GAAA,CAAI,SAAA,GAAY,EAAE,IAAA,EAAM,MAAA,EAAQ,MAAM,CAAA;AAAA,EACxC;AAEA,EAAA,SAAS,UAAU,IAAA,EAAoB;AACrC,IAAA,MAAM,EAAA,GAAK,KAAK,GAAA,EAAI;AACpB,IAAA,YAAA,CAAa,KAAA,EAAO,MAAM,EAAE,CAAA;AAC5B,IAAA,KAAA,CAAM,QAAQ,IAAA,CAAK,EAAE,MAAM,KAAA,EAAO,IAAA,EAAM,IAAI,CAAA;AAC5C,IAAA,WAAA,EAAY;AACZ,IAAA,IAAI,CAAC,KAAA,CAAM,MAAA,EAAQ,UAAA,EAAW;AAC9B,IAAA,GAAA,CAAI,SAAA,GAAY,EAAE,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AAAA,EACvC;AAIA,EAAA,SAAS,WAAA,GAAoB;AAC3B,IAAA,IAAI;AACF,MAAA,MAAA,CAAO,YAAA,CAAa,OAAA;AAAA,QAClB,UAAA;AAAA,QACA,IAAA,CAAK,SAAA,CAAU,EAAE,EAAA,EAAI,IAAA,CAAK,KAAI,EAAG,OAAA,EAAS,KAAA,CAAM,OAAA,EAAS;AAAA,OAC3D;AACA,MAAA,YAAA,EAAa;AAAA,IACf,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAEA,EAAA,SAAS,WAAA,GAA0E;AACjF,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,UAAU,CAAA;AAClD,MAAA,IAAI,CAAC,GAAA,EAAK,OAAO,EAAC;AAClB,MAAA,MAAM,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AACxB,MAAA,IAAI,CAAC,KAAK,CAAC,KAAA,CAAM,QAAQ,CAAA,CAAE,OAAO,CAAA,EAAG,OAAO,EAAC;AAC7C,MAAA,IAAI,KAAK,GAAA,EAAI,IAAK,CAAA,CAAE,EAAA,IAAM,KAAK,oBAAA,EAAsB;AACnD,QAAA,MAAA,CAAO,YAAA,CAAa,WAAW,UAAU,CAAA;AACzC,QAAA,OAAO,EAAC;AAAA,MACV;AACA,MAAA,OAAO,CAAA,CAAE,OAAA;AAAA,IACX,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,EAAC;AAAA,IACV;AAAA,EACF;AAEA,EAAA,SAAS,aAAa,IAAA,EAAoB;AACxC,IAAA,YAAA,CAAa,UAAU,IAAI,CAAA;AAAA,EAC7B;AAEA,EAAA,SAAS,YAAA,CACP,IAAA,EACA,IAAA,EACA,EAAA,EACgB;AAChB,IAAA,OAAO,WAAW,YAAA,CAAa,IAAA,EAAM,MAAM,EAAA,EAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,EAC5D;AAGA,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,WAAA,CAAY,MAAM;AAC5C,IAAA,KAAA,CAAM,KACH,gBAAA,CAAkC,gCAAgC,CAAA,CAClE,OAAA,CAAQ,CAAC,EAAA,KAAO;AACf,MAAA,MAAM,EAAA,GAAK,MAAA,CAAO,EAAA,CAAG,OAAA,CAAQ,EAAE,CAAA;AAC/B,MAAA,IAAI,IAAI,EAAA,CAAG,WAAA,GAAc,kBAAA,CAAmB,EAAA,EAAI,IAAI,MAAM,CAAA;AAAA,IAC5D,CAAC,CAAA;AAAA,EACL,GAAG,GAAM,CAAA;AAET,EAAA,SAAS,WAAkC,IAAA,EAAY;AACrD,IAAA,KAAA,CAAM,IAAA,CAAK,YAAY,IAAI,CAAA;AAI3B,IAAA,IAAI,IAAA,CAAK,UAAU,IAAA,CAAK,MAAA,KAAW,MAAM,KAAA,CAAM,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,MAAM,CAAA;AAC3E,IAAA,cAAA,EAAe;AACf,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,SAAS,cAAA,GAAuB;AAC9B,IAAA,KAAA,CAAM,IAAA,CAAK,SAAA,GAAY,KAAA,CAAM,IAAA,CAAK,YAAA;AAAA,EACpC;AAIA,EAAA,SAAS,UAAA,GAAmB;AAC1B,IAAA,KAAA,CAAM,MAAA,EAAA;AACN,IAAA,QAAA,CAAS,KAAA,CAAM,WAAA,GAAc,MAAA,CAAO,KAAA,CAAM,MAAM,CAAA;AAChD,IAAA,QAAA,CAAS,KAAA,CAAM,MAAM,OAAA,GAAU,MAAA;AAAA,EACjC;AACA,EAAA,SAAS,WAAA,GAAoB;AAC3B,IAAA,KAAA,CAAM,MAAA,GAAS,CAAA;AACf,IAAA,QAAA,CAAS,KAAA,CAAM,MAAM,OAAA,GAAU,MAAA;AAAA,EACjC;AAIA,EAAA,SAAS,gBAAA,GAAyB;AAChC,IAAA,MAAM,IAAA,GAAO,CAAC,KAAA,CAAM,QAAA;AACpB,IAAA,KAAA,CAAM,MAAM,QAAA,GAAW,IAAA;AACvB,IAAA,KAAA,CAAM,KAAK,QAAA,GAAW,IAAA;AACtB,IAAA,KAAA,CAAM,KAAA,CAAM,WAAA,GAAc,IAAA,GACtB,wCAAA,GACA,sBAAA;AAAA,EACN;AAIA,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,KAAA;AAAA,IACA,MAAA;AAAA;AAAA;AAAA,IAGA,IAAA,EAAM,CAAC,IAAA,KAAiB;AACtB,MAAA,YAAA,CAAa,IAAI,CAAA;AACjB,MAAA,OAAO,QAAQ,OAAA,EAAQ;AAAA,IACzB,CAAA;AAAA,IACA,SAAS,MAAM;AACb,MAAA,IAAI,IAAA,CAAK,KAAA,EAAO,MAAA,CAAO,YAAA,CAAa,KAAK,KAAK,CAAA;AAC9C,MAAA,IAAI,IAAA,CAAK,WAAA,EAAa,MAAA,CAAO,YAAA,CAAa,KAAK,WAAW,CAAA;AAC1D,MAAA,MAAA,CAAO,cAAc,YAAY,CAAA;AACjC,MAAA,IAAA,CAAK,MAAA,EAAO;AAAA,IACd,CAAA;AAAA,IACA,cAAc,MAAM;AAAA,GACtB;AACF;AAIA,SAAS,aAAA,GAAsB;AAC7B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,aAAa,WAAA,EAAa;AACpE,IAAA,MAAM,IAAI,MAAM,uDAAuD,CAAA;AAAA,EACzE;AACF;AAEA,SAAS,UAAU,GAAA,EAAkC;AACnD,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACzC,EAAA,IAAA,CAAK,SAAA,GAAY,YAAA;AACjB,EAAA,IAAA,CAAK,YAAA,CAAa,eAAA,EAAiB,GAAA,CAAI,QAAQ,CAAA;AAC/C,EAAA,IAAA,CAAK,YAAA,CAAa,aAAa,OAAO,CAAA;AACtC,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,kBAAkB,KAAA,EAA0C;AACnE,EAAA,MAAM,MAA8B,EAAC;AACrC,EAAA,MAAM,SAAS,KAAA,CAAM,aAAA;AACrB,EAAA,IAAI,OAAO,UAAA,EAAY,GAAA,CAAI,aAAa,MAAA,CAAO,UAAA,CAAW,MAAM,IAAA,EAAK;AACrE,EAAA,IAAI,OAAO,SAAA,EAAW,GAAA,CAAI,YAAY,MAAA,CAAO,SAAA,CAAU,MAAM,IAAA,EAAK;AAClE,EAAA,IAAI,OAAO,KAAA,EAAO,GAAA,CAAI,QAAQ,MAAA,CAAO,KAAA,CAAM,MAAM,IAAA,EAAK;AACtD,EAAA,IAAI,OAAO,KAAA,EAAO,GAAA,CAAI,QAAQ,MAAA,CAAO,KAAA,CAAM,MAAM,IAAA,EAAK;AACtD,EAAA,OAAO,GAAA;AACT","file":"chat.cjs","sourcesContent":["/**\n * Chat widget configuration types + defaults.\n *\n * Kept separate from the widget's runtime code so other modules (and the\n * top-level `nexor.initChat()` re-export) can pull types without dragging in\n * the DOM construction code.\n */\nimport type { UUID } from \"../types\";\n\n// ─── Public option / handle types ──────────────────────────────────────────\n\nexport interface ChatLeadCapture {\n /** Which fields to ask for before chatting. Defaults to [\"first_name\", \"email\"]. */\n fields?: Array<\"first_name\" | \"last_name\" | \"email\" | \"phone\">;\n /** \"before\" gates chat behind the form; \"skip\" never asks. Default \"before\". */\n mode?: \"before\" | \"skip\";\n /** Label rendered above the form. */\n label?: string;\n /** Submit button label. */\n submitLabel?: string;\n}\n\nexport interface ChatInitOptions {\n /** Workflow that handles this chat conversation. Required. */\n workflowId: UUID;\n\n // ── Identity / branding ──\n title?: string;\n subtitle?: string;\n greeting?: string;\n /** Shown when a turn fails or the server returns no reply. */\n errorText?: string;\n /** BCP-47 locale for timestamps (e.g. \"es\", \"en-US\"). Defaults to the browser. */\n locale?: string;\n /** Hex colour for bubble + accents. Defaults to #111827. */\n accentColor?: string;\n /** Text colour rendered on top of the accent. Defaults to white. */\n accentTextColor?: string;\n /** Show the \"Powered by Nexor\" footer. Defaults to true. */\n showBranding?: boolean;\n\n // ── Behaviour ──\n position?: \"bottom-right\" | \"bottom-left\";\n /** Auto-open the panel on first load. Defaults to false. */\n openOnLoad?: boolean;\n /** Coalesce rapid-fire messages: wait this many ms of quiet, then send the\n * buffered messages as one combined turn. Defaults to 700. 0 = per message. */\n debounceMs?: number;\n /** Append the widget to a custom element. Defaults to document.body. */\n container?: HTMLElement;\n\n // ── Prompt overrides forwarded to the agent for THIS widget instance ──\n systemPrompt?: string;\n clientPrompt?: string;\n\n // ── Lead linking ──\n /** Pre-known lead info (e.g. a logged-in user). Skips capture if complete. */\n lead?: {\n first_name?: string;\n last_name?: string;\n email?: string;\n phone?: string;\n metadata?: Record<string, unknown>;\n };\n capture?: ChatLeadCapture;\n /** Arbitrary metadata passed with each turn (page URL, UTM, …). */\n metadata?: Record<string, unknown>;\n\n // ── Callbacks ──\n onOpen?: () => void;\n onClose?: () => void;\n onMessage?: (msg: { role: \"user\" | \"bot\"; text: string }) => void;\n onLeadCaptured?: (leadId: UUID) => void;\n onError?: (err: Error) => void;\n}\n\nexport interface ChatHandle {\n open(): void;\n close(): void;\n toggle(): void;\n /** Programmatically send a user message. */\n send(text: string): Promise<void>;\n /** Tear down the widget and remove all DOM. */\n destroy(): void;\n /** Read the current session id (auto-generated on first open). */\n getSessionId(): string;\n}\n\n// ─── Internal resolved-config type ─────────────────────────────────────────\n\nexport interface ResolvedCfg {\n workflowId: UUID;\n title: string;\n subtitle: string;\n greeting: string;\n errorText: string;\n locale?: string;\n accentColor: string;\n accentTextColor: string;\n showBranding: boolean;\n position: \"bottom-right\" | \"bottom-left\";\n openOnLoad: boolean;\n debounceMs: number;\n container?: HTMLElement;\n systemPrompt?: string;\n clientPrompt?: string;\n lead?: ChatInitOptions[\"lead\"];\n capture: Required<ChatLeadCapture>;\n metadata?: Record<string, unknown>;\n onOpen?: () => void;\n onClose?: () => void;\n onMessage?: (msg: { role: \"user\" | \"bot\"; text: string }) => void;\n onLeadCaptured?: (leadId: UUID) => void;\n onError?: (err: Error) => void;\n}\n\n/**\n * Normalize user-provided options into a complete config object with every\n * field set. Downstream code reads from ResolvedCfg and never has to deal\n * with `undefined` for branding / behaviour fields.\n */\nexport function applyDefaults(o: ChatInitOptions): ResolvedCfg {\n return {\n workflowId: o.workflowId,\n title: o.title ?? \"Chat with us\",\n subtitle: o.subtitle ?? \"We typically reply in a few minutes\",\n greeting: o.greeting ?? \"Hi there! 👋 How can we help?\",\n errorText:\n o.errorText ?? \"Sorry, something went wrong. Please try again in a moment.\",\n locale:\n o.locale ??\n (typeof navigator !== \"undefined\" ? navigator.language : undefined),\n accentColor: o.accentColor ?? \"#111827\",\n accentTextColor: o.accentTextColor ?? \"#ffffff\",\n showBranding: o.showBranding ?? true,\n position: o.position ?? \"bottom-right\",\n openOnLoad: o.openOnLoad ?? false,\n debounceMs: o.debounceMs ?? 700,\n container: o.container,\n systemPrompt: o.systemPrompt,\n clientPrompt: o.clientPrompt,\n lead: o.lead,\n capture: {\n fields: o.capture?.fields ?? [\"first_name\", \"email\"],\n mode: o.capture?.mode ?? \"before\",\n label: o.capture?.label ?? \"Tell us a bit about you to get started:\",\n submitLabel: o.capture?.submitLabel ?? \"Start chat\",\n },\n metadata: o.metadata,\n onOpen: o.onOpen,\n onClose: o.onClose,\n onMessage: o.onMessage,\n onLeadCaptured: o.onLeadCaptured,\n onError: o.onError,\n };\n}\n\n/**\n * Does this widget need to show a lead-capture form before chat?\n * - \"skip\" mode → no\n * - Pre-known lead already covers every required field → no\n * - Otherwise → yes\n */\nexport function needsCapture(cfg: ResolvedCfg): boolean {\n if (cfg.capture.mode === \"skip\") return false;\n if (cfg.lead) {\n const have = cfg.lead;\n return cfg.capture.fields.some((f) => !have[f]);\n }\n return true;\n}\n","/**\n * Browser-side session-id management for the chat widget.\n *\n * The session id links anonymous turns to the same lead across page reloads.\n * It's persisted in localStorage with a last-activity timestamp and a sliding\n * retention window: a conversation is kept for at least SESSION_RETENTION_MS\n * after the last activity, after which a fresh session (and transcript) starts.\n * If storage is unavailable (private mode, sandboxed iframe) we fall back to a\n * per-tab id and accept that a refresh starts a new conversation.\n */\n\nconst SESSION_KEY = \"nexor.chat.session_id\";\n\n/** Keep a conversation (session + transcript) this long after the last\n * activity, so a returning visitor can resume it. Reused by the transcript\n * cache in `./index`. The host (e.g. the landing widget) decides when to\n * auto-resume vs. prompt \"continue or start new\". */\nexport const SESSION_RETENTION_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n/**\n * Return the persisted session id, generating one on first use or once the\n * retention window has lapsed. Reading it slides the window forward.\n */\nexport function ensureSessionId(): string {\n const now = Date.now();\n try {\n const rec = readRecord();\n if (rec && now - rec.ts < SESSION_RETENTION_MS) {\n writeRecord(rec.id, now); // slide the window on activity\n return rec.id;\n }\n } catch {\n /* localStorage may be unavailable (private mode, sandboxed iframe). */\n }\n const id = `sess_${randomHex(16)}`;\n try {\n writeRecord(id, now);\n } catch {\n /* ignore — id is still valid for this page load. */\n }\n return id;\n}\n\n/** Bump the session's last-activity timestamp (slides the retention window). */\nexport function touchSession(): void {\n try {\n const rec = readRecord();\n if (rec) writeRecord(rec.id, Date.now());\n } catch {\n /* ignore */\n }\n}\n\nfunction readRecord(): { id: string; ts: number } | null {\n const raw = window.localStorage.getItem(SESSION_KEY);\n if (!raw) return null;\n try {\n const o = JSON.parse(raw);\n if (o && typeof o.id === \"string\" && typeof o.ts === \"number\") return o;\n } catch {\n /* legacy value was a plain \"sess_…\" string — adopt it below. */\n }\n if (raw.startsWith(\"sess_\")) return { id: raw, ts: Date.now() };\n return null;\n}\n\nfunction writeRecord(id: string, ts: number): void {\n window.localStorage.setItem(SESSION_KEY, JSON.stringify({ id, ts }));\n}\n\n/**\n * Hex string from cryptographically-strong randomness when available, with\n * a Math.random() fallback for very old browsers.\n */\nfunction randomHex(bytes: number): string {\n const arr = new Uint8Array(bytes);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(arr);\n } else {\n for (let i = 0; i < bytes; i++) arr[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(arr, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n","/**\n * Scoped CSS for the chat widget.\n *\n * Everything is namespaced under `.nexor-chat` so it cannot leak into the host\n * page, and properties that hosts commonly override (color, font, line-height)\n * are set explicitly here so the host's reset can't bleed in.\n *\n * Animations are GPU-friendly (transform + opacity only — no layout-trashing\n * properties like top/left) and use modest durations so the widget feels\n * responsive on low-end devices.\n */\nexport const widgetCss = (accent: string, accentText: string): string => `\n.nexor-chat,\n.nexor-chat * {\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n}\n\n.nexor-chat {\n position: fixed;\n z-index: 2147483640;\n bottom: 20px;\n right: 20px;\n font-size: 14px;\n line-height: 1.4;\n color: #111;\n}\n\n.nexor-chat[data-position=\"bottom-left\"] {\n right: auto;\n left: 20px;\n}\n\n/* ── Launcher bubble ─────────────────────────────────────────────── */\n.nexor-chat__launcher {\n width: 56px;\n height: 56px;\n border-radius: 9999px;\n background: ${accent};\n color: ${accentText};\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);\n transition: transform 160ms cubic-bezier(.2,.7,.2,1.1),\n box-shadow 160ms ease,\n background 200ms ease;\n padding: 0;\n position: relative;\n animation: nexor-launcher-in 360ms cubic-bezier(.2,.7,.2,1.1) both;\n}\n.nexor-chat__launcher:hover {\n transform: translateY(-2px) scale(1.04);\n box-shadow: 0 10px 24px rgba(0, 0, 0, 0.24);\n}\n.nexor-chat__launcher:active {\n transform: translateY(0) scale(0.97);\n transition-duration: 80ms;\n}\n.nexor-chat__launcher:focus-visible {\n outline: 2px solid ${accent};\n outline-offset: 2px;\n}\n.nexor-chat[data-open=\"true\"] .nexor-chat__launcher {\n transform: scale(0.85);\n opacity: 0.75;\n}\n.nexor-chat__launcher svg { width: 24px; height: 24px; transition: transform 200ms ease; }\n.nexor-chat[data-open=\"true\"] .nexor-chat__launcher svg { transform: rotate(8deg); }\n\n.nexor-chat__unread {\n position: absolute;\n top: -4px;\n right: -4px;\n min-width: 18px;\n height: 18px;\n background: #ef4444;\n color: #fff;\n border-radius: 9999px;\n font-size: 11px;\n font-weight: 600;\n padding: 0 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 2px solid #fff;\n animation: nexor-pop-in 300ms cubic-bezier(.2,.7,.2,1.1) both;\n}\n\n@keyframes nexor-launcher-in {\n from { transform: translateY(20px) scale(0.6); opacity: 0; }\n to { transform: translateY(0) scale(1); opacity: 1; }\n}\n@keyframes nexor-pop-in {\n from { transform: scale(0); }\n to { transform: scale(1); }\n}\n\n/* ── Panel ────────────────────────────────────────────────────────── */\n.nexor-chat__panel {\n position: absolute;\n bottom: 72px;\n right: 0;\n width: 360px;\n max-width: calc(100vw - 32px);\n height: 540px;\n max-height: calc(100vh - 120px);\n background: #fff;\n border-radius: 14px;\n box-shadow: 0 24px 60px rgba(0, 0, 0, 0.22);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n transform: translateY(14px) scale(0.96);\n opacity: 0;\n pointer-events: none;\n transition: transform 220ms cubic-bezier(.2,.7,.2,1.1),\n opacity 220ms ease;\n border: 1px solid rgba(0, 0, 0, 0.06);\n /* Use visibility so screen readers don't read collapsed content */\n visibility: hidden;\n}\n.nexor-chat[data-position=\"bottom-left\"] .nexor-chat__panel {\n right: auto;\n left: 0;\n}\n.nexor-chat[data-open=\"true\"] .nexor-chat__panel {\n transform: translateY(0) scale(1);\n opacity: 1;\n pointer-events: auto;\n visibility: visible;\n}\n\n.nexor-chat__header {\n background: ${accent};\n color: ${accentText};\n padding: 14px 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.nexor-chat__title {\n font-weight: 600;\n font-size: 15px;\n margin: 0;\n}\n.nexor-chat__subtitle {\n font-size: 12px;\n opacity: 0.85;\n margin: 2px 0 0;\n}\n.nexor-chat__status-dot {\n display: inline-block;\n width: 8px;\n height: 8px;\n border-radius: 9999px;\n background: #34d399;\n margin-right: 6px;\n vertical-align: 1px;\n animation: nexor-pulse 2.4s ease-in-out infinite;\n box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.6);\n}\n@keyframes nexor-pulse {\n 0%, 100% { box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.55); }\n 50% { box-shadow: 0 0 0 6px rgba(52, 211, 153, 0); }\n}\n.nexor-chat__close {\n background: transparent;\n border: none;\n color: inherit;\n cursor: pointer;\n padding: 4px;\n border-radius: 6px;\n opacity: 0.9;\n transition: background 140ms ease, transform 140ms ease;\n}\n.nexor-chat__close:hover { background: rgba(255,255,255,0.18); opacity: 1; }\n.nexor-chat__close:active { transform: scale(0.92); }\n.nexor-chat__close svg { width: 18px; height: 18px; display: block; }\n\n/* ── Body / messages ──────────────────────────────────────────────── */\n.nexor-chat__body {\n flex: 1;\n overflow-y: auto;\n padding: 14px;\n background: #f6f7f9;\n display: flex;\n flex-direction: column;\n gap: 8px;\n scroll-behavior: smooth;\n /* Prevent layout shift when scrollbar appears */\n scrollbar-gutter: stable;\n}\n.nexor-chat__body::-webkit-scrollbar { width: 6px; }\n.nexor-chat__body::-webkit-scrollbar-thumb {\n background: rgba(0,0,0,0.15);\n border-radius: 9999px;\n}\n\n.nexor-chat__msg {\n max-width: 80%;\n padding: 8px 12px;\n border-radius: 14px;\n white-space: pre-wrap;\n word-wrap: break-word;\n animation: nexor-msg-in 240ms cubic-bezier(.2,.7,.2,1.1) both;\n will-change: transform, opacity;\n}\n.nexor-chat__msg--bot {\n background: #fff;\n align-self: flex-start;\n border: 1px solid rgba(0,0,0,0.06);\n border-bottom-left-radius: 4px;\n box-shadow: 0 1px 2px rgba(0,0,0,0.04);\n}\n.nexor-chat__msg--user {\n background: ${accent};\n color: ${accentText};\n align-self: flex-end;\n border-bottom-right-radius: 4px;\n animation-name: nexor-msg-in-user;\n}\n.nexor-chat__msg--system {\n background: transparent;\n color: #6b7280;\n font-size: 12px;\n align-self: center;\n text-align: center;\n max-width: 100%;\n animation-name: nexor-fade-in;\n}\n.nexor-chat__msg-time {\n display: block;\n margin-top: 4px;\n font-size: 10px;\n line-height: 1;\n opacity: 0.55;\n text-align: right;\n}\n.nexor-chat__msg--bot .nexor-chat__msg-time { text-align: left; }\n\n@keyframes nexor-msg-in {\n from { transform: translateY(8px) scale(0.96); opacity: 0; }\n to { transform: translateY(0) scale(1); opacity: 1; }\n}\n@keyframes nexor-msg-in-user {\n from { transform: translateY(8px) translateX(8px) scale(0.96); opacity: 0; }\n to { transform: translateY(0) translateX(0) scale(1); opacity: 1; }\n}\n@keyframes nexor-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.nexor-chat__typing {\n align-self: flex-start;\n background: #fff;\n border: 1px solid rgba(0,0,0,0.06);\n border-radius: 14px;\n border-bottom-left-radius: 4px;\n padding: 10px 14px;\n display: inline-flex;\n gap: 4px;\n animation: nexor-msg-in 240ms cubic-bezier(.2,.7,.2,1.1) both;\n}\n.nexor-chat__typing span {\n width: 6px; height: 6px; border-radius: 9999px;\n background: #9ca3af;\n display: inline-block;\n animation: nexor-blink 1.2s infinite ease-in-out;\n}\n.nexor-chat__typing span:nth-child(2) { animation-delay: 0.15s; }\n.nexor-chat__typing span:nth-child(3) { animation-delay: 0.30s; }\n@keyframes nexor-blink {\n 0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }\n 30% { opacity: 1; transform: translateY(-3px); }\n}\n\n/* ── Lead capture form ────────────────────────────────────────────── */\n.nexor-chat__capture {\n background: #fff;\n border: 1px solid rgba(0,0,0,0.06);\n border-radius: 12px;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 4px;\n animation: nexor-msg-in 280ms cubic-bezier(.2,.7,.2,1.1) both;\n box-shadow: 0 2px 8px rgba(0,0,0,0.05);\n}\n.nexor-chat__capture-label {\n font-size: 12px;\n color: #6b7280;\n margin: 0;\n}\n.nexor-chat__capture-row {\n display: flex;\n gap: 8px;\n}\n.nexor-chat__capture-row > * { flex: 1; }\n.nexor-chat__input,\n.nexor-chat__capture input {\n font: inherit;\n padding: 8px 10px;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n background: #fff;\n color: #111;\n outline: none;\n width: 100%;\n transition: border-color 140ms ease, box-shadow 140ms ease;\n}\n.nexor-chat__input:focus,\n.nexor-chat__capture input:focus {\n border-color: ${accent};\n box-shadow: 0 0 0 3px ${accent}33;\n}\n.nexor-chat__capture-submit {\n background: ${accent};\n color: ${accentText};\n border: none;\n border-radius: 8px;\n padding: 8px 12px;\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: transform 140ms ease, filter 140ms ease;\n}\n.nexor-chat__capture-submit:hover { filter: brightness(1.08); }\n.nexor-chat__capture-submit:active { transform: scale(0.97); }\n.nexor-chat__capture-submit[disabled] { opacity: 0.6; cursor: not-allowed; }\n\n/* ── Composer ─────────────────────────────────────────────────────── */\n.nexor-chat__composer {\n display: flex;\n gap: 8px;\n padding: 10px;\n background: #fff;\n border-top: 1px solid rgba(0,0,0,0.06);\n align-items: stretch;\n}\n.nexor-chat__send {\n background: ${accent};\n color: ${accentText};\n border: none;\n border-radius: 8px;\n padding: 0 14px;\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n min-width: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 140ms ease, filter 140ms ease;\n}\n.nexor-chat__send:hover { filter: brightness(1.08); }\n.nexor-chat__send:active { transform: scale(0.95); }\n.nexor-chat__send[disabled] {\n opacity: 0.6;\n cursor: not-allowed;\n transform: none;\n}\n.nexor-chat__send svg {\n width: 18px;\n height: 18px;\n transition: transform 200ms ease;\n}\n.nexor-chat__send:not([disabled]):hover svg { transform: translateX(2px); }\n\n/* Small spinner inside the send button while a turn is in flight */\n.nexor-chat__send--loading svg { display: none; }\n.nexor-chat__send--loading::after {\n content: \"\";\n width: 14px;\n height: 14px;\n border-radius: 9999px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n animation: nexor-spin 700ms linear infinite;\n}\n@keyframes nexor-spin {\n to { transform: rotate(360deg); }\n}\n\n.nexor-chat__footer {\n text-align: center;\n font-size: 11px;\n color: #9ca3af;\n padding: 6px;\n background: #fff;\n border-top: 1px solid rgba(0,0,0,0.04);\n}\n.nexor-chat__footer a {\n color: inherit;\n text-decoration: none;\n font-weight: 600;\n}\n.nexor-chat__footer a:hover { text-decoration: underline; }\n\n/* Respect users who request reduced motion. */\n@media (prefers-reduced-motion: reduce) {\n .nexor-chat,\n .nexor-chat * {\n animation-duration: 0.001ms !important;\n transition-duration: 0.001ms !important;\n }\n .nexor-chat__status-dot { animation: none; }\n}\n`;\n","/**\n * Pure DOM construction for the chat widget.\n *\n * Each `build*` function returns a fully-formed sub-tree plus refs to the\n * inner nodes the orchestrator (initChat) wires events to. No side effects\n * here other than calling `injectStyles()` once per page.\n *\n * Why split this out from index.ts?\n * - The DOM layer doesn't depend on the NexorClient or sessionId — keeping\n * it pure makes the widget easy to reason about and easy to dry-run in\n * unit tests by mocking `document`.\n * - The orchestrator becomes a 150-line state machine instead of a\n * 600-line god-function.\n */\nimport { needsCapture, type ResolvedCfg } from \"./config\";\nimport { widgetCss } from \"./styles\";\n\nconst STYLE_ATTR = \"data-nexor-chat-styles\";\n\n// ─── Stylesheet (singleton per page) ───────────────────────────────────────\n\n/**\n * Inject the widget's stylesheet once per page. If multiple widgets mount,\n * they share styles and the first widget's accent colour wins. Two widgets\n * with different accents on one page is an unsupported use case.\n */\nexport function injectStyles(accent: string, accentText: string): void {\n if (document.querySelector(`style[${STYLE_ATTR}]`)) return;\n const style = document.createElement(\"style\");\n style.setAttribute(STYLE_ATTR, \"1\");\n style.textContent = widgetCss(accent, accentText);\n document.head.appendChild(style);\n}\n\n// ─── Launcher ──────────────────────────────────────────────────────────────\n\nexport interface LauncherRefs {\n el: HTMLButtonElement;\n badge: HTMLSpanElement;\n}\n\nexport function buildLauncher(): LauncherRefs {\n const el = document.createElement(\"button\");\n el.type = \"button\";\n el.className = \"nexor-chat__launcher\";\n el.setAttribute(\"aria-label\", \"Open chat\");\n // Speech-bubble icon — SVG rather than emoji for crisp rendering at any DPR.\n el.innerHTML = `\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M4 5.5C4 4.67 4.67 4 5.5 4h13c.83 0 1.5.67 1.5 1.5v9c0 .83-.67 1.5-1.5 1.5H9l-4 4V5.5Z\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/>\n </svg>\n `;\n const badge = document.createElement(\"span\");\n badge.className = \"nexor-chat__unread\";\n badge.style.display = \"none\";\n el.appendChild(badge);\n return { el, badge };\n}\n\n// ─── Panel ─────────────────────────────────────────────────────────────────\n\ntype CaptureField = \"first_name\" | \"last_name\" | \"email\" | \"phone\";\n\nexport interface PanelRefs {\n el: HTMLDivElement;\n closeBtn: HTMLButtonElement;\n body: HTMLDivElement;\n form: HTMLFormElement;\n input: HTMLInputElement;\n send: HTMLButtonElement;\n captureForm: HTMLFormElement | null;\n captureInputs: Partial<Record<CaptureField, HTMLInputElement>>;\n}\n\nexport function buildPanel(cfg: ResolvedCfg): PanelRefs {\n const el = document.createElement(\"div\");\n el.className = \"nexor-chat__panel\";\n el.setAttribute(\"role\", \"dialog\");\n el.setAttribute(\"aria-label\", cfg.title);\n\n const { header, closeBtn } = buildHeader(cfg);\n const { body, captureForm, captureInputs } = buildBody(cfg);\n const { form, input, send } = buildComposer();\n const footer = cfg.showBranding ? buildFooter() : null;\n\n el.appendChild(header);\n el.appendChild(body);\n el.appendChild(form);\n if (footer) el.appendChild(footer);\n\n return { el, closeBtn, body, form, input, send, captureForm, captureInputs };\n}\n\nfunction buildHeader(cfg: ResolvedCfg): {\n header: HTMLDivElement;\n closeBtn: HTMLButtonElement;\n} {\n const header = document.createElement(\"div\");\n header.className = \"nexor-chat__header\";\n\n // The status dot signals \"we're live and listening\" — purely cosmetic, but\n // a strong UX signal. Hidden from assistive tech because the title carries\n // the meaning already.\n const subtitleHtml = cfg.subtitle\n ? `<p class=\"nexor-chat__subtitle\">\n <span class=\"nexor-chat__status-dot\" aria-hidden=\"true\"></span>${escapeHtml(cfg.subtitle)}\n </p>`\n : \"\";\n\n header.innerHTML = `\n <div>\n <p class=\"nexor-chat__title\">${escapeHtml(cfg.title)}</p>\n ${subtitleHtml}\n </div>\n `;\n\n const closeBtn = document.createElement(\"button\");\n closeBtn.type = \"button\";\n closeBtn.className = \"nexor-chat__close\";\n closeBtn.setAttribute(\"aria-label\", \"Close chat\");\n closeBtn.innerHTML = `\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M6 6l12 12M6 18L18 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n `;\n header.appendChild(closeBtn);\n return { header, closeBtn };\n}\n\nfunction buildBody(cfg: ResolvedCfg): {\n body: HTMLDivElement;\n captureForm: HTMLFormElement | null;\n captureInputs: Partial<Record<CaptureField, HTMLInputElement>>;\n} {\n const body = document.createElement(\"div\");\n body.className = \"nexor-chat__body\";\n const captureInputs: Partial<Record<CaptureField, HTMLInputElement>> = {};\n let captureForm: HTMLFormElement | null = null;\n\n if (needsCapture(cfg)) {\n captureForm = buildCaptureForm(cfg, captureInputs);\n body.appendChild(captureForm);\n }\n return { body, captureForm, captureInputs };\n}\n\nfunction buildCaptureForm(\n cfg: ResolvedCfg,\n inputsOut: Partial<Record<CaptureField, HTMLInputElement>>,\n): HTMLFormElement {\n const form = document.createElement(\"form\");\n form.className = \"nexor-chat__capture\";\n form.noValidate = true;\n\n const label = document.createElement(\"p\");\n label.className = \"nexor-chat__capture-label\";\n label.textContent = cfg.capture.label;\n form.appendChild(label);\n\n const fields = cfg.capture.fields;\n\n // first_name + last_name share a row to feel like a single \"your name\" prompt.\n if (fields.includes(\"first_name\") || fields.includes(\"last_name\")) {\n const row = document.createElement(\"div\");\n row.className = \"nexor-chat__capture-row\";\n if (fields.includes(\"first_name\")) {\n inputsOut.first_name = makeInput(\"first_name\", \"First name\", \"text\", cfg.lead?.first_name);\n row.appendChild(inputsOut.first_name);\n }\n if (fields.includes(\"last_name\")) {\n inputsOut.last_name = makeInput(\"last_name\", \"Last name\", \"text\", cfg.lead?.last_name);\n row.appendChild(inputsOut.last_name);\n }\n if (row.childElementCount > 0) form.appendChild(row);\n }\n if (fields.includes(\"email\")) {\n inputsOut.email = makeInput(\"email\", \"Email\", \"email\", cfg.lead?.email);\n form.appendChild(inputsOut.email);\n }\n if (fields.includes(\"phone\")) {\n inputsOut.phone = makeInput(\"phone\", \"Phone\", \"tel\", cfg.lead?.phone);\n form.appendChild(inputsOut.phone);\n }\n\n const submit = document.createElement(\"button\");\n submit.type = \"submit\";\n submit.className = \"nexor-chat__capture-submit\";\n submit.textContent = cfg.capture.submitLabel;\n form.appendChild(submit);\n\n return form;\n}\n\nfunction buildComposer(): {\n form: HTMLFormElement;\n input: HTMLInputElement;\n send: HTMLButtonElement;\n} {\n const form = document.createElement(\"form\");\n form.className = \"nexor-chat__composer\";\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.className = \"nexor-chat__input\";\n input.placeholder = \"Type a message…\";\n input.autocomplete = \"off\";\n\n const send = document.createElement(\"button\");\n send.type = \"submit\";\n send.className = \"nexor-chat__send\";\n send.setAttribute(\"aria-label\", \"Send message\");\n // Paper-airplane icon — language-agnostic, recognisable, replaces \"Send\" text.\n send.innerHTML = `\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M3.4 11.2 20.5 4.3c.7-.3 1.4.4 1.1 1.1l-6.9 17.1c-.3.8-1.4.8-1.8 0L10 14l-7.5-3c-.8-.3-.8-1.5 0-1.8Z\"\n stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linejoin=\"round\" fill=\"currentColor\"/>\n </svg>\n `;\n\n form.appendChild(input);\n form.appendChild(send);\n return { form, input, send };\n}\n\nfunction buildFooter(): HTMLDivElement {\n const el = document.createElement(\"div\");\n el.className = \"nexor-chat__footer\";\n el.innerHTML = `Powered by <a href=\"https://www.getnexor.ai\" target=\"_blank\" rel=\"noopener noreferrer\">Nexor</a>`;\n return el;\n}\n\n// ─── Message bubbles ───────────────────────────────────────────────────────\n\nexport type MessageRole = \"user\" | \"bot\" | \"system\";\n\n/**\n * Build a chat bubble. `textContent` is used (never innerHTML) so user input\n * is rendered as plain text — no XSS surface from message payloads.\n */\nexport function buildMessage(\n role: MessageRole,\n text: string,\n ts?: number,\n locale?: string,\n): HTMLDivElement {\n const el = document.createElement(\"div\");\n el.className = `nexor-chat__msg nexor-chat__msg--${role}`;\n const body = document.createElement(\"span\");\n body.className = \"nexor-chat__msg-text\";\n body.textContent = text;\n el.appendChild(body);\n // Relative time under user/bot messages (not system notices). `data-ts` lets\n // the widget refresh the label as time passes (see index.ts).\n if (role !== \"system\" && ts) {\n const time = document.createElement(\"time\");\n time.className = \"nexor-chat__msg-time\";\n time.dataset.ts = String(ts);\n time.textContent = formatRelativeTime(ts, locale);\n el.appendChild(time);\n }\n return el;\n}\n\n/**\n * Relative within the last week (\"ahora\", \"hace 3 minutos\", \"hace 2 horas\",\n * \"ayer\"), then an absolute date (\"12 may\"). Localized via Intl; falls back to\n * a plain clock time if Intl.RelativeTimeFormat is unavailable.\n */\nexport function formatRelativeTime(\n ts: number,\n locale?: string,\n now: number = Date.now(),\n): string {\n try {\n const diff = Math.max(0, now - ts);\n const min = Math.round(diff / 60000);\n const hr = Math.round(diff / 3600000);\n const day = Math.round(diff / 86400000);\n const rtf = new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n if (diff < 45000) return rtf.format(0, \"second\"); // \"ahora\" / \"now\"\n if (min < 60) return rtf.format(-min, \"minute\");\n if (hr < 24) return rtf.format(-hr, \"hour\");\n if (day < 7) return rtf.format(-day, \"day\"); // includes \"ayer\"/\"yesterday\"\n return new Date(ts).toLocaleDateString(locale, {\n day: \"numeric\",\n month: \"short\",\n });\n } catch {\n try {\n return new Date(ts).toLocaleTimeString(locale, {\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n } catch {\n return \"\";\n }\n }\n}\n\n/** Three-dot typing indicator. Animation lives in styles.ts. */\nexport function buildTypingIndicator(): HTMLDivElement {\n const el = document.createElement(\"div\");\n el.className = \"nexor-chat__typing\";\n el.innerHTML = \"<span></span><span></span><span></span>\";\n return el;\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────────\n\nfunction makeInput(\n name: string,\n placeholder: string,\n type: string,\n value?: string,\n): HTMLInputElement {\n const i = document.createElement(\"input\");\n i.name = name;\n i.type = type;\n i.placeholder = placeholder;\n if (value) i.value = value;\n return i;\n}\n\n/**\n * Escape strings before interpolation into innerHTML. Used only for\n * SDK-controlled config values (title, subtitle) — user-typed messages\n * always go through textContent, which doesn't need escaping.\n */\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n","/**\n * Chat widget orchestrator.\n *\n * Responsibilities (everything else lives in sibling files):\n * - apply defaults / validate options (`./config`)\n * - manage session id (`./session`)\n * - build DOM (`./dom`)\n * - inject CSS (`./dom`)\n *\n * What stays here:\n * - the open/close state machine\n * - wiring DOM events to the NexorClient.chat() call\n * - lead-capture submission flow\n * - the public ChatHandle returned to callers\n */\nimport type { NexorClient } from \"../client\";\nimport type { UUID } from \"../types\";\nimport {\n applyDefaults,\n needsCapture,\n type ChatInitOptions,\n type ChatHandle,\n type ResolvedCfg,\n} from \"./config\";\nimport { ensureSessionId, touchSession, SESSION_RETENTION_MS } from \"./session\";\nimport {\n buildLauncher,\n buildMessage,\n buildPanel,\n buildTypingIndicator,\n formatRelativeTime,\n injectStyles,\n type LauncherRefs,\n type MessageRole,\n type PanelRefs,\n} from \"./dom\";\n\n// Re-export the public types so consumers can `import type { ChatHandle } from \"@getnexorai/sdk\"`.\nexport type { ChatInitOptions, ChatHandle, ChatLeadCapture } from \"./config\";\n\n// Delay before showing the typing indicator. Short replies arrive before this\n// fires and never show the dots, which feels snappier than always flashing them.\nconst TYPING_DELAY_MS = 220;\n\n/**\n * Mount the chat widget and return a handle for programmatic control.\n *\n * @param client The NexorClient instance that will send turns.\n * @param options User-facing widget configuration. See ChatInitOptions.\n * @throws If called outside a browser, or without a workflowId.\n */\nexport function initChat(\n client: NexorClient,\n options: ChatInitOptions,\n): ChatHandle {\n ensureBrowser();\n if (!options || !options.workflowId) {\n throw new Error(\"Nexor SDK: initChat() requires `workflowId`.\");\n }\n\n const cfg = applyDefaults(options);\n injectStyles(cfg.accentColor, cfg.accentTextColor);\n\n const sessionId = ensureSessionId();\n const historyKey = `nexor.chat.history.${sessionId}`;\n const root = buildRoot(cfg);\n const launcher = buildLauncher();\n const panel = buildPanel(cfg);\n root.appendChild(launcher.el);\n root.appendChild(panel.el);\n (cfg.container ?? document.body).appendChild(root);\n\n // ─── State (closure-private — no external observers needed) ─────────────\n const state = {\n isOpen: false,\n unread: 0,\n leadId: undefined as UUID | undefined,\n captured: !needsCapture(cfg),\n pendingLead: { ...(cfg.lead ?? {}) },\n history: [] as Array<{ role: \"user\" | \"bot\"; text: string; ts?: number }>,\n };\n\n // ─── DOM event wiring ───────────────────────────────────────────────────\n launcher.el.addEventListener(\"click\", toggle);\n panel.closeBtn.addEventListener(\"click\", close);\n panel.form.addEventListener(\"submit\", onComposerSubmit);\n panel.captureForm?.addEventListener(\"submit\", onCaptureSubmit);\n\n syncComposerLock();\n\n // Restore a prior transcript (persisted up to SESSION_RETENTION_MS) so a\n // page reload keeps the conversation. Rendered into the (possibly closed)\n // panel; the greeting is then skipped because history is non-empty.\n const restored = loadHistory();\n if (restored.length) {\n state.history = restored;\n for (const m of restored) appendBubble(m.role === \"user\" ? \"user\" : \"bot\", m.text, m.ts);\n scrollToBottom();\n }\n\n if (cfg.openOnLoad) open();\n\n // ─── State machine ──────────────────────────────────────────────────────\n\n function open(): void {\n if (state.isOpen) return;\n state.isOpen = true;\n root.setAttribute(\"data-open\", \"true\");\n clearUnread();\n // Lazy-greet: first time the panel opens, drop the greeting message.\n if (state.history.length === 0 && cfg.greeting && state.captured) {\n appendBot(cfg.greeting);\n }\n panel.input.focus();\n cfg.onOpen?.();\n }\n\n function close(): void {\n if (!state.isOpen) return;\n state.isOpen = false;\n root.setAttribute(\"data-open\", \"false\");\n cfg.onClose?.();\n }\n\n function toggle(): void {\n state.isOpen ? close() : open();\n }\n\n // ─── Capture flow ───────────────────────────────────────────────────────\n\n function onCaptureSubmit(e: Event): void {\n e.preventDefault();\n const collected = readCaptureFields(panel);\n // Validate that all required fields are filled before unlocking the chat.\n const required = cfg.capture.fields;\n for (const f of required) {\n if (!collected[f]) {\n panel.captureInputs[f]?.focus();\n return;\n }\n }\n state.pendingLead = { ...state.pendingLead, ...collected };\n state.captured = true;\n panel.captureForm?.remove();\n syncComposerLock();\n panel.input.focus();\n // Greet once capture clears, so the greeting feels like a response to \"I'm ready\".\n if (cfg.greeting && state.history.length === 0) appendBot(cfg.greeting);\n }\n\n // ─── Composer / send (debounced + coalesced) ────────────────────────────\n //\n // Visitors fire several short messages in a row (WhatsApp-style). We mirror\n // the WhatsApp contract — batch at the source, one LLM call per batch — by\n // buffering rapid messages in the browser and POSTing them as a SINGLE\n // combined turn after a short quiet window. The composer never locks, so the\n // user can keep typing and sending freely; messages that land mid-request are\n // sent in the next batch.\n\n const send = {\n pending: [] as string[],\n timer: 0,\n typingTimer: 0,\n inFlight: false,\n typing: null as HTMLElement | null,\n };\n\n function onComposerSubmit(e: Event): void {\n e.preventDefault();\n const text = panel.input.value.trim();\n if (!text) return;\n panel.input.value = \"\";\n queueMessage(text);\n }\n\n function queueMessage(text: string): void {\n if (!state.captured) {\n panel.captureInputs.first_name?.focus();\n return;\n }\n appendUser(text);\n send.pending.push(text);\n scheduleFlush();\n }\n\n function scheduleFlush(): void {\n if (send.timer) window.clearTimeout(send.timer);\n send.timer = window.setTimeout(() => void flush(), cfg.debounceMs);\n }\n\n async function flush(): Promise<void> {\n if (send.inFlight || send.pending.length === 0) return;\n const batch = send.pending.splice(0, send.pending.length);\n const message = batch.join(\"\\n\");\n send.inFlight = true;\n\n // Delay the typing dots so fast replies don't flash them.\n send.typingTimer = window.setTimeout(() => {\n send.typing = appendNode(buildTypingIndicator());\n }, TYPING_DELAY_MS);\n\n try {\n const res = await client.chat({\n workflow_id: cfg.workflowId,\n session_id: sessionId,\n message,\n system_prompt: cfg.systemPrompt,\n client_prompt: cfg.clientPrompt,\n lead: Object.keys(state.pendingLead).length ? state.pendingLead : undefined,\n metadata: cfg.metadata,\n });\n if (res.lead_id && !state.leadId) {\n state.leadId = res.lead_id;\n cfg.onLeadCaptured?.(state.leadId);\n }\n const reply = (res.reply ?? \"\").trim();\n if (reply) {\n appendBot(reply);\n } else {\n // No text came back (empty completion / tool-only turn). Never render a\n // blank bubble — show the fallback and surface it to the host.\n appendSystem(cfg.errorText);\n cfg.onError?.(new Error(\"Nexor SDK: empty reply from server\"));\n }\n } catch (err) {\n appendSystem(cfg.errorText);\n cfg.onError?.(err as Error);\n } finally {\n window.clearTimeout(send.typingTimer);\n send.typing?.remove();\n send.typing = null;\n send.inFlight = false;\n panel.input.focus();\n // Anything queued while the request was in flight goes out next.\n if (send.pending.length) scheduleFlush();\n }\n }\n\n // ─── Message rendering ──────────────────────────────────────────────────\n\n function appendUser(text: string): void {\n const ts = Date.now();\n appendBubble(\"user\", text, ts);\n state.history.push({ role: \"user\", text, ts });\n saveHistory();\n cfg.onMessage?.({ role: \"user\", text });\n }\n\n function appendBot(text: string): void {\n const ts = Date.now();\n appendBubble(\"bot\", text, ts);\n state.history.push({ role: \"bot\", text, ts });\n saveHistory();\n if (!state.isOpen) bumpUnread();\n cfg.onMessage?.({ role: \"bot\", text });\n }\n\n // ─── Transcript persistence (survives reloads up to SESSION_RETENTION_MS) ──\n\n function saveHistory(): void {\n try {\n window.localStorage.setItem(\n historyKey,\n JSON.stringify({ ts: Date.now(), history: state.history }),\n );\n touchSession(); // keep the session alive in step with the transcript\n } catch {\n /* storage unavailable — in-memory history still works for this load. */\n }\n }\n\n function loadHistory(): Array<{ role: \"user\" | \"bot\"; text: string; ts?: number }> {\n try {\n const raw = window.localStorage.getItem(historyKey);\n if (!raw) return [];\n const o = JSON.parse(raw);\n if (!o || !Array.isArray(o.history)) return [];\n if (Date.now() - (o.ts || 0) > SESSION_RETENTION_MS) {\n window.localStorage.removeItem(historyKey);\n return [];\n }\n return o.history;\n } catch {\n return [];\n }\n }\n\n function appendSystem(text: string): void {\n appendBubble(\"system\", text);\n }\n\n function appendBubble(\n role: MessageRole,\n text: string,\n ts?: number,\n ): HTMLDivElement {\n return appendNode(buildMessage(role, text, ts, cfg.locale));\n }\n\n // Keep relative timestamps (\"hace 3 minutos\") fresh while the panel is open.\n const refreshTimer = window.setInterval(() => {\n panel.body\n .querySelectorAll<HTMLTimeElement>(\".nexor-chat__msg-time[data-ts]\")\n .forEach((el) => {\n const ts = Number(el.dataset.ts);\n if (ts) el.textContent = formatRelativeTime(ts, cfg.locale);\n });\n }, 60_000);\n\n function appendNode<T extends HTMLElement>(node: T): T {\n panel.body.appendChild(node);\n // Keep the typing indicator pinned to the bottom: if the visitor fires\n // another message while we're awaiting a reply, the dots must stay BELOW\n // it (the bot hasn't \"typed\" yet), not stranded between messages.\n if (send.typing && send.typing !== node) panel.body.appendChild(send.typing);\n scrollToBottom();\n return node;\n }\n\n function scrollToBottom(): void {\n panel.body.scrollTop = panel.body.scrollHeight;\n }\n\n // ─── Unread badge ───────────────────────────────────────────────────────\n\n function bumpUnread(): void {\n state.unread++;\n launcher.badge.textContent = String(state.unread);\n launcher.badge.style.display = \"flex\";\n }\n function clearUnread(): void {\n state.unread = 0;\n launcher.badge.style.display = \"none\";\n }\n\n // ─── Composer lock (used while capture form is visible) ─────────────────\n\n function syncComposerLock(): void {\n const lock = !state.captured;\n panel.input.disabled = lock;\n panel.send.disabled = lock;\n panel.input.placeholder = lock\n ? \"Complete the form above to start…\"\n : \"Type a message…\";\n }\n\n // ─── Public handle ──────────────────────────────────────────────────────\n\n return {\n open,\n close,\n toggle,\n // Programmatic send goes through the same debounce/coalesce path. Resolves\n // immediately — the message is queued, not necessarily sent yet.\n send: (text: string) => {\n queueMessage(text);\n return Promise.resolve();\n },\n destroy: () => {\n if (send.timer) window.clearTimeout(send.timer);\n if (send.typingTimer) window.clearTimeout(send.typingTimer);\n window.clearInterval(refreshTimer);\n root.remove();\n },\n getSessionId: () => sessionId,\n };\n}\n\n// ─── Module-scoped helpers ────────────────────────────────────────────────\n\nfunction ensureBrowser(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n throw new Error(\"Nexor SDK: initChat() requires a browser environment.\");\n }\n}\n\nfunction buildRoot(cfg: ResolvedCfg): HTMLDivElement {\n const root = document.createElement(\"div\");\n root.className = \"nexor-chat\";\n root.setAttribute(\"data-position\", cfg.position);\n root.setAttribute(\"data-open\", \"false\");\n return root;\n}\n\nfunction readCaptureFields(panel: PanelRefs): Record<string, string> {\n const out: Record<string, string> = {};\n const inputs = panel.captureInputs;\n if (inputs.first_name) out.first_name = inputs.first_name.value.trim();\n if (inputs.last_name) out.last_name = inputs.last_name.value.trim();\n if (inputs.email) out.email = inputs.email.value.trim();\n if (inputs.phone) out.phone = inputs.phone.value.trim();\n return out;\n}\n\n// Used by initChat's launcher → keeps the ref objects in scope for TS.\ntype _Refs = LauncherRefs & PanelRefs;\nvoid (null as unknown as _Refs);\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/chat/config.ts","../src/chat/session.ts","../src/chat/styles.ts","../src/chat/dom.ts","../src/chat/index.ts"],"names":[],"mappings":"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","file":"chat.cjs","sourcesContent":["/**\n * Chat widget configuration types + defaults.\n *\n * Kept separate from the widget's runtime code so other modules (and the\n * top-level `nexor.initChat()` re-export) can pull types without dragging in\n * the DOM construction code.\n */\nimport type { UUID } from \"../types\";\n\n// ─── Public option / handle types ──────────────────────────────────────────\n\nexport interface ChatLeadCapture {\n /** Which fields to ask for before chatting. Defaults to [\"first_name\", \"email\"]. */\n fields?: Array<\"first_name\" | \"last_name\" | \"email\" | \"phone\">;\n /** \"before\" gates chat behind the form; \"skip\" never asks. Default \"before\". */\n mode?: \"before\" | \"skip\";\n /** Label rendered above the form. */\n label?: string;\n /** Submit button label. */\n submitLabel?: string;\n}\n\nexport interface ChatInitOptions {\n /** Workflow that handles this chat conversation. Required. */\n workflowId: UUID;\n\n // ── Identity / branding ──\n title?: string;\n subtitle?: string;\n greeting?: string;\n /** Shown when a turn fails or the server returns no reply. */\n errorText?: string;\n /** BCP-47 locale for timestamps (e.g. \"es\", \"en-US\"). Defaults to the browser. */\n locale?: string;\n /** Hex colour for bubble + accents. Defaults to #111827. */\n accentColor?: string;\n /** Text colour rendered on top of the accent. Defaults to white. */\n accentTextColor?: string;\n /** Show the \"Powered by Nexor\" footer. Defaults to true. */\n showBranding?: boolean;\n\n // ── Behaviour ──\n position?: \"bottom-right\" | \"bottom-left\";\n /** Auto-open the panel on first load. Defaults to false. */\n openOnLoad?: boolean;\n /** Coalesce rapid-fire messages: wait this many ms of quiet, then send the\n * buffered messages as one combined turn. Defaults to 700. 0 = per message. */\n debounceMs?: number;\n /** Per-turn request timeout (ms). Agent turns can legitimately take 10-20s,\n * so this is generous (default 60000). Chat turns never retry — a timeout\n * surfaces the error once rather than re-sending the message. */\n requestTimeoutMs?: number;\n /** Append the widget to a custom element. Defaults to document.body. */\n container?: HTMLElement;\n\n // ── Prompt overrides forwarded to the agent for THIS widget instance ──\n systemPrompt?: string;\n clientPrompt?: string;\n\n // ── Lead linking ──\n /** Pre-known lead info (e.g. a logged-in user). Skips capture if complete. */\n lead?: {\n first_name?: string;\n last_name?: string;\n email?: string;\n phone?: string;\n metadata?: Record<string, unknown>;\n };\n capture?: ChatLeadCapture;\n /** Arbitrary metadata passed with each turn (page URL, UTM, …). */\n metadata?: Record<string, unknown>;\n\n // ── Callbacks ──\n onOpen?: () => void;\n onClose?: () => void;\n onMessage?: (msg: { role: \"user\" | \"bot\"; text: string }) => void;\n onLeadCaptured?: (leadId: UUID) => void;\n onError?: (err: Error) => void;\n}\n\nexport interface ChatHandle {\n open(): void;\n close(): void;\n toggle(): void;\n /** Programmatically send a user message. */\n send(text: string): Promise<void>;\n /** Tear down the widget and remove all DOM. */\n destroy(): void;\n /** Read the current session id (auto-generated on first open). */\n getSessionId(): string;\n}\n\n// ─── Internal resolved-config type ─────────────────────────────────────────\n\nexport interface ResolvedCfg {\n workflowId: UUID;\n title: string;\n subtitle: string;\n greeting: string;\n errorText: string;\n locale?: string;\n accentColor: string;\n accentTextColor: string;\n showBranding: boolean;\n position: \"bottom-right\" | \"bottom-left\";\n openOnLoad: boolean;\n debounceMs: number;\n requestTimeoutMs: number;\n container?: HTMLElement;\n systemPrompt?: string;\n clientPrompt?: string;\n lead?: ChatInitOptions[\"lead\"];\n capture: Required<ChatLeadCapture>;\n metadata?: Record<string, unknown>;\n onOpen?: () => void;\n onClose?: () => void;\n onMessage?: (msg: { role: \"user\" | \"bot\"; text: string }) => void;\n onLeadCaptured?: (leadId: UUID) => void;\n onError?: (err: Error) => void;\n}\n\n/**\n * Normalize user-provided options into a complete config object with every\n * field set. Downstream code reads from ResolvedCfg and never has to deal\n * with `undefined` for branding / behaviour fields.\n */\nexport function applyDefaults(o: ChatInitOptions): ResolvedCfg {\n return {\n workflowId: o.workflowId,\n title: o.title ?? \"Chat with us\",\n subtitle: o.subtitle ?? \"We typically reply in a few minutes\",\n greeting: o.greeting ?? \"Hi there! 👋 How can we help?\",\n errorText:\n o.errorText ?? \"Sorry, something went wrong. Please try again in a moment.\",\n locale:\n o.locale ??\n (typeof navigator !== \"undefined\" ? navigator.language : undefined),\n accentColor: o.accentColor ?? \"#111827\",\n accentTextColor: o.accentTextColor ?? \"#ffffff\",\n showBranding: o.showBranding ?? true,\n position: o.position ?? \"bottom-right\",\n openOnLoad: o.openOnLoad ?? false,\n debounceMs: o.debounceMs ?? 700,\n requestTimeoutMs: o.requestTimeoutMs ?? 60_000,\n container: o.container,\n systemPrompt: o.systemPrompt,\n clientPrompt: o.clientPrompt,\n lead: o.lead,\n capture: {\n fields: o.capture?.fields ?? [\"first_name\", \"email\"],\n mode: o.capture?.mode ?? \"before\",\n label: o.capture?.label ?? \"Tell us a bit about you to get started:\",\n submitLabel: o.capture?.submitLabel ?? \"Start chat\",\n },\n metadata: o.metadata,\n onOpen: o.onOpen,\n onClose: o.onClose,\n onMessage: o.onMessage,\n onLeadCaptured: o.onLeadCaptured,\n onError: o.onError,\n };\n}\n\n/**\n * Does this widget need to show a lead-capture form before chat?\n * - \"skip\" mode → no\n * - Pre-known lead already covers every required field → no\n * - Otherwise → yes\n */\nexport function needsCapture(cfg: ResolvedCfg): boolean {\n if (cfg.capture.mode === \"skip\") return false;\n if (cfg.lead) {\n const have = cfg.lead;\n return cfg.capture.fields.some((f) => !have[f]);\n }\n return true;\n}\n","/**\n * Browser-side session-id management for the chat widget.\n *\n * The session id links anonymous turns to the same lead across page reloads.\n * It's persisted in localStorage with a last-activity timestamp and a sliding\n * retention window: a conversation is kept for at least SESSION_RETENTION_MS\n * after the last activity, after which a fresh session (and transcript) starts.\n * If storage is unavailable (private mode, sandboxed iframe) we fall back to a\n * per-tab id and accept that a refresh starts a new conversation.\n */\n\nconst SESSION_KEY = \"nexor.chat.session_id\";\n\n/** Keep a conversation (session + transcript) this long after the last\n * activity, so a returning visitor can resume it. Reused by the transcript\n * cache in `./index`. The host (e.g. the landing widget) decides when to\n * auto-resume vs. prompt \"continue or start new\". */\nexport const SESSION_RETENTION_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\n\n/**\n * Return the persisted session id, generating one on first use or once the\n * retention window has lapsed. Reading it slides the window forward.\n */\nexport function ensureSessionId(): string {\n const now = Date.now();\n try {\n const rec = readRecord();\n if (rec && now - rec.ts < SESSION_RETENTION_MS) {\n writeRecord(rec.id, now); // slide the window on activity\n return rec.id;\n }\n } catch {\n /* localStorage may be unavailable (private mode, sandboxed iframe). */\n }\n const id = `sess_${randomHex(16)}`;\n try {\n writeRecord(id, now);\n } catch {\n /* ignore — id is still valid for this page load. */\n }\n return id;\n}\n\n/** Bump the session's last-activity timestamp (slides the retention window). */\nexport function touchSession(): void {\n try {\n const rec = readRecord();\n if (rec) writeRecord(rec.id, Date.now());\n } catch {\n /* ignore */\n }\n}\n\nfunction readRecord(): { id: string; ts: number } | null {\n const raw = window.localStorage.getItem(SESSION_KEY);\n if (!raw) return null;\n try {\n const o = JSON.parse(raw);\n if (o && typeof o.id === \"string\" && typeof o.ts === \"number\") return o;\n } catch {\n /* legacy value was a plain \"sess_…\" string — adopt it below. */\n }\n if (raw.startsWith(\"sess_\")) return { id: raw, ts: Date.now() };\n return null;\n}\n\nfunction writeRecord(id: string, ts: number): void {\n window.localStorage.setItem(SESSION_KEY, JSON.stringify({ id, ts }));\n}\n\n/**\n * Hex string from cryptographically-strong randomness when available, with\n * a Math.random() fallback for very old browsers.\n */\nfunction randomHex(bytes: number): string {\n const arr = new Uint8Array(bytes);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(arr);\n } else {\n for (let i = 0; i < bytes; i++) arr[i] = Math.floor(Math.random() * 256);\n }\n return Array.from(arr, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n","/**\n * Scoped CSS for the chat widget.\n *\n * Everything is namespaced under `.nexor-chat` so it cannot leak into the host\n * page, and properties that hosts commonly override (color, font, line-height)\n * are set explicitly here so the host's reset can't bleed in.\n *\n * Animations are GPU-friendly (transform + opacity only — no layout-trashing\n * properties like top/left) and use modest durations so the widget feels\n * responsive on low-end devices.\n */\nexport const widgetCss = (accent: string, accentText: string): string => `\n.nexor-chat,\n.nexor-chat * {\n box-sizing: border-box;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n}\n\n.nexor-chat {\n position: fixed;\n z-index: 2147483640;\n bottom: 20px;\n right: 20px;\n font-size: 14px;\n line-height: 1.4;\n color: #111;\n}\n\n.nexor-chat[data-position=\"bottom-left\"] {\n right: auto;\n left: 20px;\n}\n\n/* ── Launcher bubble ─────────────────────────────────────────────── */\n.nexor-chat__launcher {\n width: 56px;\n height: 56px;\n border-radius: 9999px;\n background: ${accent};\n color: ${accentText};\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);\n transition: transform 160ms cubic-bezier(.2,.7,.2,1.1),\n box-shadow 160ms ease,\n background 200ms ease;\n padding: 0;\n position: relative;\n animation: nexor-launcher-in 360ms cubic-bezier(.2,.7,.2,1.1) both;\n}\n.nexor-chat__launcher:hover {\n transform: translateY(-2px) scale(1.04);\n box-shadow: 0 10px 24px rgba(0, 0, 0, 0.24);\n}\n.nexor-chat__launcher:active {\n transform: translateY(0) scale(0.97);\n transition-duration: 80ms;\n}\n.nexor-chat__launcher:focus-visible {\n outline: 2px solid ${accent};\n outline-offset: 2px;\n}\n.nexor-chat[data-open=\"true\"] .nexor-chat__launcher {\n transform: scale(0.85);\n opacity: 0.75;\n}\n.nexor-chat__launcher svg { width: 24px; height: 24px; transition: transform 200ms ease; }\n.nexor-chat[data-open=\"true\"] .nexor-chat__launcher svg { transform: rotate(8deg); }\n\n.nexor-chat__unread {\n position: absolute;\n top: -4px;\n right: -4px;\n min-width: 18px;\n height: 18px;\n background: #ef4444;\n color: #fff;\n border-radius: 9999px;\n font-size: 11px;\n font-weight: 600;\n padding: 0 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 2px solid #fff;\n animation: nexor-pop-in 300ms cubic-bezier(.2,.7,.2,1.1) both;\n}\n\n@keyframes nexor-launcher-in {\n from { transform: translateY(20px) scale(0.6); opacity: 0; }\n to { transform: translateY(0) scale(1); opacity: 1; }\n}\n@keyframes nexor-pop-in {\n from { transform: scale(0); }\n to { transform: scale(1); }\n}\n\n/* ── Panel ────────────────────────────────────────────────────────── */\n.nexor-chat__panel {\n position: absolute;\n bottom: 72px;\n right: 0;\n width: 360px;\n max-width: calc(100vw - 32px);\n height: 540px;\n max-height: calc(100vh - 120px);\n background: #fff;\n border-radius: 14px;\n box-shadow: 0 24px 60px rgba(0, 0, 0, 0.22);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n transform: translateY(14px) scale(0.96);\n opacity: 0;\n pointer-events: none;\n transition: transform 220ms cubic-bezier(.2,.7,.2,1.1),\n opacity 220ms ease;\n border: 1px solid rgba(0, 0, 0, 0.06);\n /* Use visibility so screen readers don't read collapsed content */\n visibility: hidden;\n}\n.nexor-chat[data-position=\"bottom-left\"] .nexor-chat__panel {\n right: auto;\n left: 0;\n}\n.nexor-chat[data-open=\"true\"] .nexor-chat__panel {\n transform: translateY(0) scale(1);\n opacity: 1;\n pointer-events: auto;\n visibility: visible;\n}\n\n.nexor-chat__header {\n background: ${accent};\n color: ${accentText};\n padding: 14px 16px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.nexor-chat__title {\n font-weight: 600;\n font-size: 15px;\n margin: 0;\n}\n.nexor-chat__subtitle {\n font-size: 12px;\n opacity: 0.85;\n margin: 2px 0 0;\n}\n.nexor-chat__status-dot {\n display: inline-block;\n width: 8px;\n height: 8px;\n border-radius: 9999px;\n background: #34d399;\n margin-right: 6px;\n vertical-align: 1px;\n animation: nexor-pulse 2.4s ease-in-out infinite;\n box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.6);\n}\n@keyframes nexor-pulse {\n 0%, 100% { box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.55); }\n 50% { box-shadow: 0 0 0 6px rgba(52, 211, 153, 0); }\n}\n.nexor-chat__close {\n background: transparent;\n border: none;\n color: inherit;\n cursor: pointer;\n padding: 4px;\n border-radius: 6px;\n opacity: 0.9;\n transition: background 140ms ease, transform 140ms ease;\n}\n.nexor-chat__close:hover { background: rgba(255,255,255,0.18); opacity: 1; }\n.nexor-chat__close:active { transform: scale(0.92); }\n.nexor-chat__close svg { width: 18px; height: 18px; display: block; }\n\n/* ── Body / messages ──────────────────────────────────────────────── */\n.nexor-chat__body {\n flex: 1;\n overflow-y: auto;\n padding: 14px;\n background: #f6f7f9;\n display: flex;\n flex-direction: column;\n gap: 8px;\n scroll-behavior: smooth;\n /* Prevent layout shift when scrollbar appears */\n scrollbar-gutter: stable;\n}\n.nexor-chat__body::-webkit-scrollbar { width: 6px; }\n.nexor-chat__body::-webkit-scrollbar-thumb {\n background: rgba(0,0,0,0.15);\n border-radius: 9999px;\n}\n\n.nexor-chat__msg {\n max-width: 80%;\n padding: 8px 12px;\n border-radius: 14px;\n white-space: pre-wrap;\n word-wrap: break-word;\n animation: nexor-msg-in 240ms cubic-bezier(.2,.7,.2,1.1) both;\n will-change: transform, opacity;\n}\n.nexor-chat__msg--bot {\n background: #fff;\n align-self: flex-start;\n border: 1px solid rgba(0,0,0,0.06);\n border-bottom-left-radius: 4px;\n box-shadow: 0 1px 2px rgba(0,0,0,0.04);\n}\n.nexor-chat__msg--user {\n background: ${accent};\n color: ${accentText};\n align-self: flex-end;\n border-bottom-right-radius: 4px;\n animation-name: nexor-msg-in-user;\n}\n.nexor-chat__msg--system {\n background: transparent;\n color: #6b7280;\n font-size: 12px;\n align-self: center;\n text-align: center;\n max-width: 100%;\n animation-name: nexor-fade-in;\n}\n.nexor-chat__msg-time {\n display: block;\n margin-top: 4px;\n font-size: 10px;\n line-height: 1;\n opacity: 0.55;\n text-align: right;\n}\n.nexor-chat__msg--bot .nexor-chat__msg-time { text-align: left; }\n\n@keyframes nexor-msg-in {\n from { transform: translateY(8px) scale(0.96); opacity: 0; }\n to { transform: translateY(0) scale(1); opacity: 1; }\n}\n@keyframes nexor-msg-in-user {\n from { transform: translateY(8px) translateX(8px) scale(0.96); opacity: 0; }\n to { transform: translateY(0) translateX(0) scale(1); opacity: 1; }\n}\n@keyframes nexor-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.nexor-chat__typing {\n align-self: flex-start;\n background: #fff;\n border: 1px solid rgba(0,0,0,0.06);\n border-radius: 14px;\n border-bottom-left-radius: 4px;\n padding: 10px 14px;\n display: inline-flex;\n gap: 4px;\n animation: nexor-msg-in 240ms cubic-bezier(.2,.7,.2,1.1) both;\n}\n.nexor-chat__typing span {\n width: 6px; height: 6px; border-radius: 9999px;\n background: #9ca3af;\n display: inline-block;\n animation: nexor-blink 1.2s infinite ease-in-out;\n}\n.nexor-chat__typing span:nth-child(2) { animation-delay: 0.15s; }\n.nexor-chat__typing span:nth-child(3) { animation-delay: 0.30s; }\n@keyframes nexor-blink {\n 0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }\n 30% { opacity: 1; transform: translateY(-3px); }\n}\n\n/* ── Lead capture form ────────────────────────────────────────────── */\n.nexor-chat__capture {\n background: #fff;\n border: 1px solid rgba(0,0,0,0.06);\n border-radius: 12px;\n padding: 12px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 4px;\n animation: nexor-msg-in 280ms cubic-bezier(.2,.7,.2,1.1) both;\n box-shadow: 0 2px 8px rgba(0,0,0,0.05);\n}\n.nexor-chat__capture-label {\n font-size: 12px;\n color: #6b7280;\n margin: 0;\n}\n.nexor-chat__capture-row {\n display: flex;\n gap: 8px;\n}\n.nexor-chat__capture-row > * { flex: 1; }\n.nexor-chat__input,\n.nexor-chat__capture input {\n font: inherit;\n padding: 8px 10px;\n border: 1px solid #d1d5db;\n border-radius: 8px;\n background: #fff;\n color: #111;\n outline: none;\n width: 100%;\n transition: border-color 140ms ease, box-shadow 140ms ease;\n}\n.nexor-chat__input:focus,\n.nexor-chat__capture input:focus {\n border-color: ${accent};\n box-shadow: 0 0 0 3px ${accent}33;\n}\n.nexor-chat__capture-submit {\n background: ${accent};\n color: ${accentText};\n border: none;\n border-radius: 8px;\n padding: 8px 12px;\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: transform 140ms ease, filter 140ms ease;\n}\n.nexor-chat__capture-submit:hover { filter: brightness(1.08); }\n.nexor-chat__capture-submit:active { transform: scale(0.97); }\n.nexor-chat__capture-submit[disabled] { opacity: 0.6; cursor: not-allowed; }\n\n/* ── Composer ─────────────────────────────────────────────────────── */\n.nexor-chat__composer {\n display: flex;\n gap: 8px;\n padding: 10px;\n background: #fff;\n border-top: 1px solid rgba(0,0,0,0.06);\n align-items: stretch;\n}\n.nexor-chat__send {\n background: ${accent};\n color: ${accentText};\n border: none;\n border-radius: 8px;\n padding: 0 14px;\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n min-width: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 140ms ease, filter 140ms ease;\n}\n.nexor-chat__send:hover { filter: brightness(1.08); }\n.nexor-chat__send:active { transform: scale(0.95); }\n.nexor-chat__send[disabled] {\n opacity: 0.6;\n cursor: not-allowed;\n transform: none;\n}\n.nexor-chat__send svg {\n width: 18px;\n height: 18px;\n transition: transform 200ms ease;\n}\n.nexor-chat__send:not([disabled]):hover svg { transform: translateX(2px); }\n\n/* Small spinner inside the send button while a turn is in flight */\n.nexor-chat__send--loading svg { display: none; }\n.nexor-chat__send--loading::after {\n content: \"\";\n width: 14px;\n height: 14px;\n border-radius: 9999px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n animation: nexor-spin 700ms linear infinite;\n}\n@keyframes nexor-spin {\n to { transform: rotate(360deg); }\n}\n\n.nexor-chat__footer {\n text-align: center;\n font-size: 11px;\n color: #9ca3af;\n padding: 6px;\n background: #fff;\n border-top: 1px solid rgba(0,0,0,0.04);\n}\n.nexor-chat__footer a {\n color: inherit;\n text-decoration: none;\n font-weight: 600;\n}\n.nexor-chat__footer a:hover { text-decoration: underline; }\n\n/* Respect users who request reduced motion. */\n@media (prefers-reduced-motion: reduce) {\n .nexor-chat,\n .nexor-chat * {\n animation-duration: 0.001ms !important;\n transition-duration: 0.001ms !important;\n }\n .nexor-chat__status-dot { animation: none; }\n}\n`;\n","/**\n * Pure DOM construction for the chat widget.\n *\n * Each `build*` function returns a fully-formed sub-tree plus refs to the\n * inner nodes the orchestrator (initChat) wires events to. No side effects\n * here other than calling `injectStyles()` once per page.\n *\n * Why split this out from index.ts?\n * - The DOM layer doesn't depend on the NexorClient or sessionId — keeping\n * it pure makes the widget easy to reason about and easy to dry-run in\n * unit tests by mocking `document`.\n * - The orchestrator becomes a 150-line state machine instead of a\n * 600-line god-function.\n */\nimport { needsCapture, type ResolvedCfg } from \"./config\";\nimport { widgetCss } from \"./styles\";\n\nconst STYLE_ATTR = \"data-nexor-chat-styles\";\n\n// ─── Stylesheet (singleton per page) ───────────────────────────────────────\n\n/**\n * Inject the widget's stylesheet once per page. If multiple widgets mount,\n * they share styles and the first widget's accent colour wins. Two widgets\n * with different accents on one page is an unsupported use case.\n */\nexport function injectStyles(accent: string, accentText: string): void {\n if (document.querySelector(`style[${STYLE_ATTR}]`)) return;\n const style = document.createElement(\"style\");\n style.setAttribute(STYLE_ATTR, \"1\");\n style.textContent = widgetCss(accent, accentText);\n document.head.appendChild(style);\n}\n\n// ─── Launcher ──────────────────────────────────────────────────────────────\n\nexport interface LauncherRefs {\n el: HTMLButtonElement;\n badge: HTMLSpanElement;\n}\n\nexport function buildLauncher(): LauncherRefs {\n const el = document.createElement(\"button\");\n el.type = \"button\";\n el.className = \"nexor-chat__launcher\";\n el.setAttribute(\"aria-label\", \"Open chat\");\n // Speech-bubble icon — SVG rather than emoji for crisp rendering at any DPR.\n el.innerHTML = `\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M4 5.5C4 4.67 4.67 4 5.5 4h13c.83 0 1.5.67 1.5 1.5v9c0 .83-.67 1.5-1.5 1.5H9l-4 4V5.5Z\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/>\n </svg>\n `;\n const badge = document.createElement(\"span\");\n badge.className = \"nexor-chat__unread\";\n badge.style.display = \"none\";\n el.appendChild(badge);\n return { el, badge };\n}\n\n// ─── Panel ─────────────────────────────────────────────────────────────────\n\ntype CaptureField = \"first_name\" | \"last_name\" | \"email\" | \"phone\";\n\nexport interface PanelRefs {\n el: HTMLDivElement;\n closeBtn: HTMLButtonElement;\n body: HTMLDivElement;\n form: HTMLFormElement;\n input: HTMLInputElement;\n send: HTMLButtonElement;\n captureForm: HTMLFormElement | null;\n captureInputs: Partial<Record<CaptureField, HTMLInputElement>>;\n}\n\nexport function buildPanel(cfg: ResolvedCfg): PanelRefs {\n const el = document.createElement(\"div\");\n el.className = \"nexor-chat__panel\";\n el.setAttribute(\"role\", \"dialog\");\n el.setAttribute(\"aria-label\", cfg.title);\n\n const { header, closeBtn } = buildHeader(cfg);\n const { body, captureForm, captureInputs } = buildBody(cfg);\n const { form, input, send } = buildComposer();\n const footer = cfg.showBranding ? buildFooter() : null;\n\n el.appendChild(header);\n el.appendChild(body);\n el.appendChild(form);\n if (footer) el.appendChild(footer);\n\n return { el, closeBtn, body, form, input, send, captureForm, captureInputs };\n}\n\nfunction buildHeader(cfg: ResolvedCfg): {\n header: HTMLDivElement;\n closeBtn: HTMLButtonElement;\n} {\n const header = document.createElement(\"div\");\n header.className = \"nexor-chat__header\";\n\n // The status dot signals \"we're live and listening\" — purely cosmetic, but\n // a strong UX signal. Hidden from assistive tech because the title carries\n // the meaning already.\n const subtitleHtml = cfg.subtitle\n ? `<p class=\"nexor-chat__subtitle\">\n <span class=\"nexor-chat__status-dot\" aria-hidden=\"true\"></span>${escapeHtml(cfg.subtitle)}\n </p>`\n : \"\";\n\n header.innerHTML = `\n <div>\n <p class=\"nexor-chat__title\">${escapeHtml(cfg.title)}</p>\n ${subtitleHtml}\n </div>\n `;\n\n const closeBtn = document.createElement(\"button\");\n closeBtn.type = \"button\";\n closeBtn.className = \"nexor-chat__close\";\n closeBtn.setAttribute(\"aria-label\", \"Close chat\");\n closeBtn.innerHTML = `\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M6 6l12 12M6 18L18 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n </svg>\n `;\n header.appendChild(closeBtn);\n return { header, closeBtn };\n}\n\nfunction buildBody(cfg: ResolvedCfg): {\n body: HTMLDivElement;\n captureForm: HTMLFormElement | null;\n captureInputs: Partial<Record<CaptureField, HTMLInputElement>>;\n} {\n const body = document.createElement(\"div\");\n body.className = \"nexor-chat__body\";\n const captureInputs: Partial<Record<CaptureField, HTMLInputElement>> = {};\n let captureForm: HTMLFormElement | null = null;\n\n if (needsCapture(cfg)) {\n captureForm = buildCaptureForm(cfg, captureInputs);\n body.appendChild(captureForm);\n }\n return { body, captureForm, captureInputs };\n}\n\nfunction buildCaptureForm(\n cfg: ResolvedCfg,\n inputsOut: Partial<Record<CaptureField, HTMLInputElement>>,\n): HTMLFormElement {\n const form = document.createElement(\"form\");\n form.className = \"nexor-chat__capture\";\n form.noValidate = true;\n\n const label = document.createElement(\"p\");\n label.className = \"nexor-chat__capture-label\";\n label.textContent = cfg.capture.label;\n form.appendChild(label);\n\n const fields = cfg.capture.fields;\n\n // first_name + last_name share a row to feel like a single \"your name\" prompt.\n if (fields.includes(\"first_name\") || fields.includes(\"last_name\")) {\n const row = document.createElement(\"div\");\n row.className = \"nexor-chat__capture-row\";\n if (fields.includes(\"first_name\")) {\n inputsOut.first_name = makeInput(\"first_name\", \"First name\", \"text\", cfg.lead?.first_name);\n row.appendChild(inputsOut.first_name);\n }\n if (fields.includes(\"last_name\")) {\n inputsOut.last_name = makeInput(\"last_name\", \"Last name\", \"text\", cfg.lead?.last_name);\n row.appendChild(inputsOut.last_name);\n }\n if (row.childElementCount > 0) form.appendChild(row);\n }\n if (fields.includes(\"email\")) {\n inputsOut.email = makeInput(\"email\", \"Email\", \"email\", cfg.lead?.email);\n form.appendChild(inputsOut.email);\n }\n if (fields.includes(\"phone\")) {\n inputsOut.phone = makeInput(\"phone\", \"Phone\", \"tel\", cfg.lead?.phone);\n form.appendChild(inputsOut.phone);\n }\n\n const submit = document.createElement(\"button\");\n submit.type = \"submit\";\n submit.className = \"nexor-chat__capture-submit\";\n submit.textContent = cfg.capture.submitLabel;\n form.appendChild(submit);\n\n return form;\n}\n\nfunction buildComposer(): {\n form: HTMLFormElement;\n input: HTMLInputElement;\n send: HTMLButtonElement;\n} {\n const form = document.createElement(\"form\");\n form.className = \"nexor-chat__composer\";\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.className = \"nexor-chat__input\";\n input.placeholder = \"Type a message…\";\n input.autocomplete = \"off\";\n\n const send = document.createElement(\"button\");\n send.type = \"submit\";\n send.className = \"nexor-chat__send\";\n send.setAttribute(\"aria-label\", \"Send message\");\n // Paper-airplane icon — language-agnostic, recognisable, replaces \"Send\" text.\n send.innerHTML = `\n <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M3.4 11.2 20.5 4.3c.7-.3 1.4.4 1.1 1.1l-6.9 17.1c-.3.8-1.4.8-1.8 0L10 14l-7.5-3c-.8-.3-.8-1.5 0-1.8Z\"\n stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linejoin=\"round\" fill=\"currentColor\"/>\n </svg>\n `;\n\n form.appendChild(input);\n form.appendChild(send);\n return { form, input, send };\n}\n\nfunction buildFooter(): HTMLDivElement {\n const el = document.createElement(\"div\");\n el.className = \"nexor-chat__footer\";\n el.innerHTML = `Powered by <a href=\"https://www.getnexor.ai\" target=\"_blank\" rel=\"noopener noreferrer\">Nexor</a>`;\n return el;\n}\n\n// ─── Message bubbles ───────────────────────────────────────────────────────\n\nexport type MessageRole = \"user\" | \"bot\" | \"system\";\n\n/**\n * Build a chat bubble. `textContent` is used (never innerHTML) so user input\n * is rendered as plain text — no XSS surface from message payloads.\n */\nexport function buildMessage(\n role: MessageRole,\n text: string,\n ts?: number,\n locale?: string,\n): HTMLDivElement {\n const el = document.createElement(\"div\");\n el.className = `nexor-chat__msg nexor-chat__msg--${role}`;\n const body = document.createElement(\"span\");\n body.className = \"nexor-chat__msg-text\";\n body.textContent = text;\n el.appendChild(body);\n // Relative time under user/bot messages (not system notices). `data-ts` lets\n // the widget refresh the label as time passes (see index.ts).\n if (role !== \"system\" && ts) {\n const time = document.createElement(\"time\");\n time.className = \"nexor-chat__msg-time\";\n time.dataset.ts = String(ts);\n time.textContent = formatRelativeTime(ts, locale);\n el.appendChild(time);\n }\n return el;\n}\n\n/**\n * Relative within the last week (\"ahora\", \"hace 3 minutos\", \"hace 2 horas\",\n * \"ayer\"), then an absolute date (\"12 may\"). Localized via Intl; falls back to\n * a plain clock time if Intl.RelativeTimeFormat is unavailable.\n */\nexport function formatRelativeTime(\n ts: number,\n locale?: string,\n now: number = Date.now(),\n): string {\n try {\n const diff = Math.max(0, now - ts);\n const min = Math.round(diff / 60000);\n const hr = Math.round(diff / 3600000);\n const day = Math.round(diff / 86400000);\n const rtf = new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n if (diff < 45000) return rtf.format(0, \"second\"); // \"ahora\" / \"now\"\n if (min < 60) return rtf.format(-min, \"minute\");\n if (hr < 24) return rtf.format(-hr, \"hour\");\n if (day < 7) return rtf.format(-day, \"day\"); // includes \"ayer\"/\"yesterday\"\n return new Date(ts).toLocaleDateString(locale, {\n day: \"numeric\",\n month: \"short\",\n });\n } catch {\n try {\n return new Date(ts).toLocaleTimeString(locale, {\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n } catch {\n return \"\";\n }\n }\n}\n\n/** Three-dot typing indicator. Animation lives in styles.ts. */\nexport function buildTypingIndicator(): HTMLDivElement {\n const el = document.createElement(\"div\");\n el.className = \"nexor-chat__typing\";\n el.innerHTML = \"<span></span><span></span><span></span>\";\n return el;\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────────\n\nfunction makeInput(\n name: string,\n placeholder: string,\n type: string,\n value?: string,\n): HTMLInputElement {\n const i = document.createElement(\"input\");\n i.name = name;\n i.type = type;\n i.placeholder = placeholder;\n if (value) i.value = value;\n return i;\n}\n\n/**\n * Escape strings before interpolation into innerHTML. Used only for\n * SDK-controlled config values (title, subtitle) — user-typed messages\n * always go through textContent, which doesn't need escaping.\n */\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n","/**\n * Chat widget orchestrator.\n *\n * Responsibilities (everything else lives in sibling files):\n * - apply defaults / validate options (`./config`)\n * - manage session id (`./session`)\n * - build DOM (`./dom`)\n * - inject CSS (`./dom`)\n *\n * What stays here:\n * - the open/close state machine\n * - wiring DOM events to the NexorClient.chat() call\n * - lead-capture submission flow\n * - the public ChatHandle returned to callers\n */\nimport type { NexorClient } from \"../client\";\nimport type { UUID } from \"../types\";\nimport {\n applyDefaults,\n needsCapture,\n type ChatInitOptions,\n type ChatHandle,\n type ResolvedCfg,\n} from \"./config\";\nimport { ensureSessionId, touchSession, SESSION_RETENTION_MS } from \"./session\";\nimport {\n buildLauncher,\n buildMessage,\n buildPanel,\n buildTypingIndicator,\n formatRelativeTime,\n injectStyles,\n type LauncherRefs,\n type MessageRole,\n type PanelRefs,\n} from \"./dom\";\n\n// Re-export the public types so consumers can `import type { ChatHandle } from \"@getnexorai/sdk\"`.\nexport type { ChatInitOptions, ChatHandle, ChatLeadCapture } from \"./config\";\n\n/**\n * Mount the chat widget and return a handle for programmatic control.\n *\n * @param client The NexorClient instance that will send turns.\n * @param options User-facing widget configuration. See ChatInitOptions.\n * @throws If called outside a browser, or without a workflowId.\n */\nexport function initChat(\n client: NexorClient,\n options: ChatInitOptions,\n): ChatHandle {\n ensureBrowser();\n if (!options || !options.workflowId) {\n throw new Error(\"Nexor SDK: initChat() requires `workflowId`.\");\n }\n\n const cfg = applyDefaults(options);\n injectStyles(cfg.accentColor, cfg.accentTextColor);\n\n const sessionId = ensureSessionId();\n const historyKey = `nexor.chat.history.${sessionId}`;\n const root = buildRoot(cfg);\n const launcher = buildLauncher();\n const panel = buildPanel(cfg);\n root.appendChild(launcher.el);\n root.appendChild(panel.el);\n (cfg.container ?? document.body).appendChild(root);\n\n // ─── State (closure-private — no external observers needed) ─────────────\n const state = {\n isOpen: false,\n unread: 0,\n leadId: undefined as UUID | undefined,\n captured: !needsCapture(cfg),\n pendingLead: { ...(cfg.lead ?? {}) },\n history: [] as Array<{ role: \"user\" | \"bot\"; text: string; ts?: number }>,\n };\n\n // Composer/send state, hoisted above DOM wiring + history restore so the\n // call chain `restore → appendBubble → appendNode → send.typing` (and the\n // openOnLoad → appendBot greeting path) doesn't hit a TDZ. See the\n // debounced/coalesced send logic further down for how this is used.\n const send = {\n pending: [] as string[],\n timer: 0,\n inFlight: false,\n typing: null as HTMLElement | null,\n };\n\n // First-impression delay: the lazy-greet bubble used to appear instantly,\n // which read as canned. Show typing dots for ~2s first so the greeting feels\n // composed in the moment. Cancelled if the visitor sends before it fires.\n const greeting = { pending: false, timer: 0 };\n const GREETING_DELAY_MS = 2000;\n\n // ─── DOM event wiring ───────────────────────────────────────────────────\n launcher.el.addEventListener(\"click\", toggle);\n panel.closeBtn.addEventListener(\"click\", close);\n panel.form.addEventListener(\"submit\", onComposerSubmit);\n panel.captureForm?.addEventListener(\"submit\", onCaptureSubmit);\n\n syncComposerLock();\n\n // Restore a prior transcript (persisted up to SESSION_RETENTION_MS) so a\n // page reload keeps the conversation. Rendered into the (possibly closed)\n // panel; the greeting is then skipped because history is non-empty.\n const restored = loadHistory();\n if (restored.length) {\n state.history = restored;\n for (const m of restored) appendBubble(m.role === \"user\" ? \"user\" : \"bot\", m.text, m.ts);\n scrollToBottom();\n }\n\n if (cfg.openOnLoad) open();\n\n // ─── State machine ──────────────────────────────────────────────────────\n\n function open(): void {\n if (state.isOpen) return;\n state.isOpen = true;\n root.setAttribute(\"data-open\", \"true\");\n clearUnread();\n // Lazy-greet: first time the panel opens, drop the greeting message.\n if (state.history.length === 0 && cfg.greeting && state.captured) {\n scheduleGreeting();\n }\n panel.input.focus();\n cfg.onOpen?.();\n }\n\n function close(): void {\n if (!state.isOpen) return;\n state.isOpen = false;\n root.setAttribute(\"data-open\", \"false\");\n cfg.onClose?.();\n }\n\n function toggle(): void {\n state.isOpen ? close() : open();\n }\n\n // ─── Capture flow ───────────────────────────────────────────────────────\n\n function onCaptureSubmit(e: Event): void {\n e.preventDefault();\n const collected = readCaptureFields(panel);\n // Validate that all required fields are filled before unlocking the chat.\n const required = cfg.capture.fields;\n for (const f of required) {\n if (!collected[f]) {\n panel.captureInputs[f]?.focus();\n return;\n }\n }\n state.pendingLead = { ...state.pendingLead, ...collected };\n state.captured = true;\n panel.captureForm?.remove();\n syncComposerLock();\n panel.input.focus();\n // Greet once capture clears, so the greeting feels like a response to \"I'm ready\".\n if (cfg.greeting && state.history.length === 0) scheduleGreeting();\n }\n\n // ─── Composer / send (debounced + coalesced) ────────────────────────────\n //\n // Visitors fire several short messages in a row (WhatsApp-style). We mirror\n // the WhatsApp contract — batch at the source, one LLM call per batch — by\n // buffering rapid messages in the browser and POSTing them as a SINGLE\n // combined turn after a short quiet window. The composer never locks, so the\n // user can keep typing and sending freely; messages that land mid-request are\n // sent in the next batch. (`const send` itself is declared near `state`\n // above so DOM-wiring callbacks don't hit a TDZ on it.)\n\n function onComposerSubmit(e: Event): void {\n e.preventDefault();\n const text = panel.input.value.trim();\n if (!text) return;\n panel.input.value = \"\";\n queueMessage(text);\n }\n\n function queueMessage(text: string): void {\n if (!state.captured) {\n panel.captureInputs.first_name?.focus();\n return;\n }\n // Visitor engaged before the greeting fired — drop it. Showing a canned\n // greeting *after* their first real message would feel out of order.\n cancelGreeting();\n appendUser(text);\n send.pending.push(text);\n scheduleFlush();\n }\n\n function scheduleGreeting(): void {\n if (greeting.pending) return;\n greeting.pending = true;\n showTyping();\n greeting.timer = window.setTimeout(() => {\n greeting.pending = false;\n greeting.timer = 0;\n hideTyping();\n appendBot(cfg.greeting!);\n }, GREETING_DELAY_MS);\n }\n\n function cancelGreeting(): void {\n if (!greeting.pending) return;\n if (greeting.timer) window.clearTimeout(greeting.timer);\n greeting.timer = 0;\n greeting.pending = false;\n hideTyping();\n }\n\n // Show the typing dots the INSTANT a message is queued. We still hold the\n // actual send for the quiet window, but the visitor gets immediate \"I heard\n // you, I'm responding\" feedback — the dots persist across the debounce + the\n // request, so there's never a dead 2.5s gap after they hit send.\n function showTyping(): void {\n if (send.typing) {\n // Already up — keep it pinned below the latest message.\n panel.body.appendChild(send.typing);\n scrollToBottom();\n return;\n }\n send.typing = appendNode(buildTypingIndicator());\n }\n\n function hideTyping(): void {\n send.typing?.remove();\n send.typing = null;\n }\n\n function scheduleFlush(): void {\n showTyping();\n if (send.timer) window.clearTimeout(send.timer);\n send.timer = window.setTimeout(() => void flush(), cfg.debounceMs);\n }\n\n async function flush(): Promise<void> {\n if (send.inFlight || send.pending.length === 0) return;\n const batch = send.pending.splice(0, send.pending.length);\n const message = batch.join(\"\\n\");\n send.inFlight = true;\n\n try {\n const res = await client.chat(\n {\n workflow_id: cfg.workflowId,\n session_id: sessionId,\n message,\n system_prompt: cfg.systemPrompt,\n client_prompt: cfg.clientPrompt,\n lead: Object.keys(state.pendingLead).length ? state.pendingLead : undefined,\n metadata: cfg.metadata,\n },\n // A chat turn is NOT idempotent (re-sending re-runs the LLM and\n // re-inserts the message). Never retry; just give it a generous timeout.\n { timeoutMs: cfg.requestTimeoutMs, maxRetries: 0 },\n );\n if (res.lead_id && !state.leadId) {\n state.leadId = res.lead_id;\n cfg.onLeadCaptured?.(state.leadId);\n }\n const reply = (res.reply ?? \"\").trim();\n hideTyping(); // clear the dots right before the reply lands\n if (reply) {\n appendBot(reply);\n } else {\n // No text came back (empty completion / tool-only turn). Never render a\n // blank bubble — show the fallback and surface it to the host.\n appendSystem(cfg.errorText);\n cfg.onError?.(new Error(\"Nexor SDK: empty reply from server\"));\n }\n } catch (err) {\n hideTyping();\n appendSystem(cfg.errorText);\n cfg.onError?.(err as Error);\n } finally {\n hideTyping(); // safety net — never leave dangling dots\n send.inFlight = false;\n panel.input.focus();\n // Anything queued while the request was in flight goes out next.\n if (send.pending.length) scheduleFlush();\n }\n }\n\n // ─── Message rendering ──────────────────────────────────────────────────\n\n function appendUser(text: string): void {\n const ts = Date.now();\n appendBubble(\"user\", text, ts);\n state.history.push({ role: \"user\", text, ts });\n saveHistory();\n cfg.onMessage?.({ role: \"user\", text });\n }\n\n function appendBot(text: string): void {\n const ts = Date.now();\n appendBubble(\"bot\", text, ts);\n state.history.push({ role: \"bot\", text, ts });\n saveHistory();\n if (!state.isOpen) bumpUnread();\n cfg.onMessage?.({ role: \"bot\", text });\n }\n\n // ─── Transcript persistence (survives reloads up to SESSION_RETENTION_MS) ──\n\n function saveHistory(): void {\n try {\n window.localStorage.setItem(\n historyKey,\n JSON.stringify({ ts: Date.now(), history: state.history }),\n );\n touchSession(); // keep the session alive in step with the transcript\n } catch {\n /* storage unavailable — in-memory history still works for this load. */\n }\n }\n\n function loadHistory(): Array<{ role: \"user\" | \"bot\"; text: string; ts?: number }> {\n try {\n const raw = window.localStorage.getItem(historyKey);\n if (!raw) return [];\n const o = JSON.parse(raw);\n if (!o || !Array.isArray(o.history)) return [];\n if (Date.now() - (o.ts || 0) > SESSION_RETENTION_MS) {\n window.localStorage.removeItem(historyKey);\n return [];\n }\n return o.history;\n } catch {\n return [];\n }\n }\n\n function appendSystem(text: string): void {\n appendBubble(\"system\", text);\n }\n\n function appendBubble(\n role: MessageRole,\n text: string,\n ts?: number,\n ): HTMLDivElement {\n return appendNode(buildMessage(role, text, ts, cfg.locale));\n }\n\n // Keep relative timestamps (\"hace 3 minutos\") fresh while the panel is open.\n const refreshTimer = window.setInterval(() => {\n panel.body\n .querySelectorAll<HTMLTimeElement>(\".nexor-chat__msg-time[data-ts]\")\n .forEach((el) => {\n const ts = Number(el.dataset.ts);\n if (ts) el.textContent = formatRelativeTime(ts, cfg.locale);\n });\n }, 60_000);\n\n function appendNode<T extends HTMLElement>(node: T): T {\n panel.body.appendChild(node);\n // Keep the typing indicator pinned to the bottom: if the visitor fires\n // another message while we're awaiting a reply, the dots must stay BELOW\n // it (the bot hasn't \"typed\" yet), not stranded between messages.\n if (send.typing && send.typing !== node) panel.body.appendChild(send.typing);\n scrollToBottom();\n return node;\n }\n\n function scrollToBottom(): void {\n panel.body.scrollTop = panel.body.scrollHeight;\n }\n\n // ─── Unread badge ───────────────────────────────────────────────────────\n\n function bumpUnread(): void {\n state.unread++;\n launcher.badge.textContent = String(state.unread);\n launcher.badge.style.display = \"flex\";\n }\n function clearUnread(): void {\n state.unread = 0;\n launcher.badge.style.display = \"none\";\n }\n\n // ─── Composer lock (used while capture form is visible) ─────────────────\n\n function syncComposerLock(): void {\n const lock = !state.captured;\n panel.input.disabled = lock;\n panel.send.disabled = lock;\n panel.input.placeholder = lock\n ? \"Complete the form above to start…\"\n : \"Type a message…\";\n }\n\n // ─── Public handle ──────────────────────────────────────────────────────\n\n return {\n open,\n close,\n toggle,\n // Programmatic send goes through the same debounce/coalesce path. Resolves\n // immediately — the message is queued, not necessarily sent yet.\n send: (text: string) => {\n queueMessage(text);\n return Promise.resolve();\n },\n destroy: () => {\n if (send.timer) window.clearTimeout(send.timer);\n if (greeting.timer) window.clearTimeout(greeting.timer);\n window.clearInterval(refreshTimer);\n root.remove();\n },\n getSessionId: () => sessionId,\n };\n}\n\n// ─── Module-scoped helpers ────────────────────────────────────────────────\n\nfunction ensureBrowser(): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n throw new Error(\"Nexor SDK: initChat() requires a browser environment.\");\n }\n}\n\nfunction buildRoot(cfg: ResolvedCfg): HTMLDivElement {\n const root = document.createElement(\"div\");\n root.className = \"nexor-chat\";\n root.setAttribute(\"data-position\", cfg.position);\n root.setAttribute(\"data-open\", \"false\");\n return root;\n}\n\nfunction readCaptureFields(panel: PanelRefs): Record<string, string> {\n const out: Record<string, string> = {};\n const inputs = panel.captureInputs;\n if (inputs.first_name) out.first_name = inputs.first_name.value.trim();\n if (inputs.last_name) out.last_name = inputs.last_name.value.trim();\n if (inputs.email) out.email = inputs.email.value.trim();\n if (inputs.phone) out.phone = inputs.phone.value.trim();\n return out;\n}\n\n// Used by initChat's launcher → keeps the ref objects in scope for TS.\ntype _Refs = LauncherRefs & PanelRefs;\nvoid (null as unknown as _Refs);\n"]}
|
package/dist/chat.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { N as NexorClient, b as ChatInitOptions, a as ChatHandle } from './config-
|
|
2
|
-
export { c as ChatLeadCapture } from './config-
|
|
1
|
+
import { N as NexorClient, b as ChatInitOptions, a as ChatHandle } from './config-khGNZDHI.cjs';
|
|
2
|
+
export { c as ChatLeadCapture } from './config-khGNZDHI.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Chat widget orchestrator.
|
package/dist/chat.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { N as NexorClient, b as ChatInitOptions, a as ChatHandle } from './config-
|
|
2
|
-
export { c as ChatLeadCapture } from './config-
|
|
1
|
+
import { N as NexorClient, b as ChatInitOptions, a as ChatHandle } from './config-khGNZDHI.js';
|
|
2
|
+
export { c as ChatLeadCapture } from './config-khGNZDHI.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Chat widget orchestrator.
|
package/dist/chat.js
CHANGED