@ibti-tech/chatbot 0.6.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -59,6 +59,38 @@ var dispatchAssitantAnswer = /* @__PURE__ */ __name(() => {
59
59
  document.dispatchEvent(evt);
60
60
  }, "dispatchAssitantAnswer");
61
61
 
62
+ // src/services/api/http-client.ts
63
+ var getDefaultHeaders = /* @__PURE__ */ __name(() => ({
64
+ "Content-Type": "application/json",
65
+ "User-Agent": typeof navigator !== "undefined" ? navigator.userAgent : "IBTI-Chatbot/1.0"
66
+ }), "getDefaultHeaders");
67
+
68
+ // src/services/chatbot/chatbot.api.ts
69
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
70
+ var cache = /* @__PURE__ */ new Map();
71
+ function cacheKey(apiURL, publicHash) {
72
+ const base = apiURL.replace(/\/+$/, "");
73
+ return `${base}:${publicHash}`;
74
+ }
75
+ __name(cacheKey, "cacheKey");
76
+ var getChatbotByPublicHash = /* @__PURE__ */ __name(async (apiURL, publicHash) => {
77
+ const key = cacheKey(apiURL, publicHash);
78
+ const now = Date.now();
79
+ const entry = cache.get(key);
80
+ if (entry && entry.expires > now) return entry.data;
81
+ const response = await fetch(`${apiURL}/chatbots/public/${publicHash}`, {
82
+ method: "GET",
83
+ headers: getDefaultHeaders(),
84
+ cache: "default"
85
+ });
86
+ if (!response.ok) {
87
+ throw new Error(`Chatbot not found (${response.status})`);
88
+ }
89
+ const data = await response.json();
90
+ cache.set(key, { data, expires: now + CACHE_TTL_MS });
91
+ return data;
92
+ }, "getChatbotByPublicHash");
93
+
62
94
  // src/i18n/en.json
63
95
  var en_default = {
64
96
  CHATBOT_NAME: "IBTI Chatbot (Beta)",
@@ -71,7 +103,9 @@ var en_default = {
71
103
  },
72
104
  WRITING_MESSAGE: "Writing",
73
105
  ERRORS: {
74
- UNKNOWN: "Sorry. We couldn't provide a response."
106
+ UNKNOWN: "Sorry. We couldn't provide a response.",
107
+ DOMAIN_NOT_ALLOWED: "The chatbot is not available on this domain.",
108
+ CHATBOT_NOT_FOUND: "Chatbot not found. Please check the configuration."
75
109
  },
76
110
  INPUT_PLACEHOLDER: "Ask something",
77
111
  CHATBOT_BAR: {
@@ -110,7 +144,9 @@ var pt_BR_default = {
110
144
  },
111
145
  WRITING_MESSAGE: "Escrevendo",
112
146
  ERRORS: {
113
- UNKNOWN: "Desculpe. N\xE3o conseguimos fornecer uma resposta."
147
+ UNKNOWN: "Desculpe. N\xE3o conseguimos fornecer uma resposta.",
148
+ DOMAIN_NOT_ALLOWED: "O chatbot n\xE3o est\xE1 dispon\xEDvel neste dom\xEDnio.",
149
+ CHATBOT_NOT_FOUND: "Chatbot n\xE3o encontrado. Verifique a configura\xE7\xE3o."
114
150
  },
115
151
  INPUT_PLACEHOLDER: "Pergunte alguma coisa",
116
152
  CHATBOT_BAR: {
@@ -143,115 +179,137 @@ var i18n = {
143
179
  "pt-BR": pt_BR_default
144
180
  };
145
181
 
146
- // src/services/chatbot-api.ts
182
+ // src/services/chat/chat.api.ts
147
183
  var sendChatContext = /* @__PURE__ */ __name(async ({
148
184
  chatContext,
149
185
  locale = "en",
150
186
  onReceiving,
151
187
  onDone,
152
- apiURL
153
- }) => fetch(`${apiURL}/chat/completion?locale=${locale}`, {
154
- method: "POST",
155
- headers: {
156
- "Content-type": "application/json"
157
- },
158
- body: JSON.stringify({
159
- context: chatContext
160
- })
161
- }).then((response) => {
162
- if (!response.ok)
188
+ apiURL,
189
+ publicHash,
190
+ visitorId
191
+ }) => {
192
+ const params = new URLSearchParams({ locale });
193
+ if (publicHash) params.set("hash", publicHash);
194
+ if (visitorId) params.set("visitorId", visitorId);
195
+ const response = await fetch(
196
+ `${apiURL}/chat/completion?${params.toString()}`,
197
+ {
198
+ method: "POST",
199
+ headers: getDefaultHeaders(),
200
+ body: JSON.stringify({ context: chatContext })
201
+ }
202
+ );
203
+ if (!response.ok) {
163
204
  throw new Error(
164
205
  i18n[locale].ERRORS.UNKNOWN + ` (status: ${response.status})`
165
206
  );
207
+ }
166
208
  if (!response.body) return;
167
209
  const reader = response.body.getReader();
168
210
  const decoder = new TextDecoder();
169
211
  let message = "";
170
- function read() {
171
- reader.read().then(({ done, value }) => {
172
- if (done) {
173
- onDone && onDone();
174
- return;
175
- }
176
- const decodifiedChunk = decoder.decode(value, { stream: true });
177
- message += decodifiedChunk;
178
- onReceiving(message, decodifiedChunk);
179
- read();
180
- });
181
- }
182
- __name(read, "read");
183
- read();
184
- }), "sendChatContext");
185
- var sendUserChatFeedback = /* @__PURE__ */ __name(async ({
186
- apiURL,
187
- chatContext,
188
- locale = "en",
189
- ratingScore,
190
- description
191
- }) => fetch(`${apiURL}/chat/feedback`, {
192
- method: "POST",
193
- headers: {
194
- "Content-type": "application/json"
195
- },
196
- body: JSON.stringify({
197
- locale,
198
- rating_score: ratingScore,
199
- chat_context: chatContext,
200
- message: description
201
- })
202
- }), "sendUserChatFeedback");
203
- var checkApiHealth = /* @__PURE__ */ __name(async (apiUrl, locale = "en") => {
212
+ const read = /* @__PURE__ */ __name(() => reader.read().then(({ done, value }) => {
213
+ if (done) {
214
+ onDone?.();
215
+ return;
216
+ }
217
+ const chunk = decoder.decode(value, { stream: true });
218
+ message += chunk;
219
+ onReceiving(message, chunk);
220
+ return read();
221
+ }), "read");
222
+ return read();
223
+ }, "sendChatContext");
224
+ var checkApiHealth = /* @__PURE__ */ __name(async (apiUrl, locale = "en", publicHash) => {
225
+ const params = new URLSearchParams({ locale });
226
+ if (publicHash) params.set("hash", publicHash);
227
+ const url = `${apiUrl}/chat/welcome?${params.toString()}`;
204
228
  const controller = new AbortController();
205
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
229
+ const timeoutMs = 5e3;
230
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
206
231
  try {
207
- const req = await fetch(`${apiUrl}/chat/welcome?locale=${locale}`, {
232
+ const response = await fetch(url, {
208
233
  method: "HEAD",
209
- // Use HEAD to minimize data transfer
210
- signal: controller.signal
234
+ signal: controller.signal,
235
+ headers: getDefaultHeaders()
211
236
  });
212
237
  clearTimeout(timeoutId);
213
- return req.ok;
214
- } catch (err) {
238
+ return response.ok;
239
+ } catch {
215
240
  clearTimeout(timeoutId);
216
- const fallbackController = new AbortController();
217
- const fallbackTimeoutId = setTimeout(
218
- () => fallbackController.abort(),
219
- 5e3
220
- );
221
- try {
222
- const req = await fetch(`${apiUrl}/chat/welcome?locale=${locale}`, {
223
- method: "GET",
224
- signal: fallbackController.signal
225
- });
226
- clearTimeout(fallbackTimeoutId);
227
- return req.ok;
228
- } catch {
229
- clearTimeout(fallbackTimeoutId);
230
- return false;
231
- }
241
+ }
242
+ const fallbackController = new AbortController();
243
+ const fallbackTimeoutId = setTimeout(
244
+ () => fallbackController.abort(),
245
+ timeoutMs
246
+ );
247
+ try {
248
+ const response = await fetch(url, {
249
+ method: "GET",
250
+ signal: fallbackController.signal,
251
+ headers: getDefaultHeaders()
252
+ });
253
+ clearTimeout(fallbackTimeoutId);
254
+ return response.ok;
255
+ } catch {
256
+ clearTimeout(fallbackTimeoutId);
257
+ return false;
232
258
  }
233
259
  }, "checkApiHealth");
234
- var getWelcomeMessage = /* @__PURE__ */ __name(async (apiUrl, locale = "en") => {
235
- const req = await fetch(`${apiUrl}/chat/welcome?locale=${locale}`, {
236
- method: "GET"
237
- });
238
- if (!req.ok) {
260
+ var getWelcomeMessage = /* @__PURE__ */ __name(async (apiUrl, locale = "en", publicHash) => {
261
+ const params = new URLSearchParams({ locale });
262
+ if (publicHash) params.set("hash", publicHash);
263
+ const response = await fetch(
264
+ `${apiUrl}/chat/welcome?${params.toString()}`,
265
+ {
266
+ method: "GET",
267
+ headers: getDefaultHeaders()
268
+ }
269
+ );
270
+ if (!response.ok) {
239
271
  throw new Error(
240
- i18n[locale].ERRORS.UNKNOWN + ` (status: ${req.status})`
272
+ i18n[locale].ERRORS.UNKNOWN + ` (status: ${response.status})`
241
273
  );
242
274
  }
243
- const data = await req.json();
275
+ const data = await response.json();
244
276
  return data.welcomeMessage;
245
277
  }, "getWelcomeMessage");
246
278
 
279
+ // src/services/feedback/feedback.api.ts
280
+ var sendUserChatFeedback = /* @__PURE__ */ __name(async ({
281
+ apiURL,
282
+ chatContext,
283
+ locale = "en",
284
+ ratingScore,
285
+ description,
286
+ publicHash,
287
+ visitorId
288
+ }) => {
289
+ return fetch(`${apiURL}/chat/feedback`, {
290
+ method: "POST",
291
+ headers: getDefaultHeaders(),
292
+ body: JSON.stringify({
293
+ locale,
294
+ rating_score: ratingScore,
295
+ chat_context: chatContext,
296
+ message: description,
297
+ ...publicHash && { hash: publicHash },
298
+ ...visitorId && { visitorId }
299
+ })
300
+ });
301
+ }, "sendUserChatFeedback");
302
+
247
303
  // src/cookies/chat.tsx
248
304
  import { parseCookies, setCookie } from "nookies";
249
305
  var ChatCookieManager = class {
250
- constructor() {
251
- this.storageKey = "ibtiChatbot@chat";
252
- this.cookieKey = "ibtiChatbot@chat";
253
- // Keep for migration purposes
306
+ // Keep for migration purposes
307
+ constructor(keySuffix) {
254
308
  this.maxAgeInHours = 24;
309
+ const base = "ibtiChatbot@chat";
310
+ const suffix = keySuffix ? `:${keySuffix}` : "";
311
+ this.storageKey = `${base}${suffix}`;
312
+ this.cookieKey = `${base}${suffix}`;
255
313
  }
256
314
  static {
257
315
  __name(this, "ChatCookieManager");
@@ -420,10 +478,13 @@ var useI18n = /* @__PURE__ */ __name(() => {
420
478
  // src/contexts/Chatbot/useChatbotMessages.ts
421
479
  var useChatbotMessages = /* @__PURE__ */ __name(({
422
480
  apiURL,
423
- locale
481
+ locale,
482
+ publicHash,
483
+ visitorId
424
484
  }) => {
425
485
  const i18n2 = useI18n();
426
- const chatCookie = new ChatCookieManager();
486
+ const cookieKeySuffix = publicHash && apiURL ? `${apiURL.replace(/\/+$/, "")}:${publicHash}` : void 0;
487
+ const chatCookie = new ChatCookieManager(cookieKeySuffix);
427
488
  const [chatMessages, setChatMessages] = useState2(
428
489
  []
429
490
  );
@@ -525,6 +586,8 @@ var useChatbotMessages = /* @__PURE__ */ __name(({
525
586
  await sendChatContext({
526
587
  apiURL,
527
588
  locale,
589
+ publicHash,
590
+ visitorId,
528
591
  chatContext: [
529
592
  ...chatMessages.map((item) => ({
530
593
  role: item.role,
@@ -596,8 +659,11 @@ var useChatbotMessages = /* @__PURE__ */ __name(({
596
659
  };
597
660
  }, []);
598
661
  const init = /* @__PURE__ */ __name(async () => {
662
+ if (publicHash !== void 0 && visitorId === void 0) {
663
+ return;
664
+ }
599
665
  try {
600
- const isApiAvailable = await checkApiHealth(apiURL, locale);
666
+ const isApiAvailable = await checkApiHealth(apiURL, locale, publicHash);
601
667
  if (!isApiAvailable) {
602
668
  setApiConnectionError(true);
603
669
  setInitialized(true);
@@ -616,7 +682,7 @@ var useChatbotMessages = /* @__PURE__ */ __name(({
616
682
  setInitialized(true);
617
683
  } else {
618
684
  try {
619
- const firstMessage = await getWelcomeMessage(apiURL, locale);
685
+ const firstMessage = await getWelcomeMessage(apiURL, locale, publicHash);
620
686
  setChatMessages([
621
687
  {
622
688
  id: uuidv4(),
@@ -639,8 +705,9 @@ var useChatbotMessages = /* @__PURE__ */ __name(({
639
705
  }, "init");
640
706
  useEffect(() => {
641
707
  if (initialized) return;
708
+ if (publicHash !== void 0 && visitorId === void 0) return;
642
709
  init();
643
- }, [initialized]);
710
+ }, [initialized, publicHash, visitorId]);
644
711
  return {
645
712
  chatMessages,
646
713
  makeQuestion,
@@ -659,17 +726,156 @@ var themes = {
659
726
  light: defaultLightTheme,
660
727
  dark: defaultDarkTheme
661
728
  };
729
+ function mergeThemeWithCustomColors(baseTheme, customColors) {
730
+ const has = /* @__PURE__ */ __name((v) => v && v.trim().length > 0, "has");
731
+ if (!customColors || !Object.values(customColors).some(has)) {
732
+ return baseTheme;
733
+ }
734
+ const primary = has(customColors.primaryColor) ? customColors.primaryColor : baseTheme.colors.palette.primary.normal;
735
+ const headerBg = has(customColors.headerBackground) ? customColors.headerBackground : has(customColors.primaryColor) ? customColors.primaryColor : baseTheme.colors.palette.primary.normal;
736
+ const userBalloon = has(customColors.userBalloonColor) ? customColors.userBalloonColor : has(customColors.primaryColor) ? customColors.primaryColor : baseTheme.colors.palette.primary.normal;
737
+ return {
738
+ ...baseTheme,
739
+ colors: {
740
+ ...baseTheme.colors,
741
+ palette: {
742
+ ...baseTheme.colors.palette,
743
+ primary: {
744
+ ...baseTheme.colors.palette.primary,
745
+ normal: primary
746
+ }
747
+ },
748
+ custom: {
749
+ headerBackground: headerBg,
750
+ userBalloonColor: userBalloon
751
+ }
752
+ }
753
+ };
754
+ }
755
+ __name(mergeThemeWithCustomColors, "mergeThemeWithCustomColors");
662
756
 
663
757
  // src/contexts/Chatbot/provider.tsx
664
758
  import { ThemeProvider } from "styled-components";
759
+
760
+ // src/utils/domainValidation.ts
761
+ var ALLOWED_ORIGINS_WITHOUT_DOMAIN_CHECK = [
762
+ "localhost",
763
+ "127.0.0.1",
764
+ "null",
765
+ "https://admin-chatbot.ibti.tech",
766
+ "https://admin-chatbot.ibti.tech/",
767
+ "http://admin-chatbot.ibti.tech",
768
+ "http://admin-chatbot.ibti.tech/"
769
+ ];
770
+ function normalizeHost(host) {
771
+ const lower = host.toLowerCase().trim();
772
+ if (lower.startsWith("localhost") || lower.startsWith("127.0.0.1")) {
773
+ return lower.split(":")[0] ?? lower;
774
+ }
775
+ return lower;
776
+ }
777
+ __name(normalizeHost, "normalizeHost");
778
+ function normalizeDomainName(domainName) {
779
+ return domainName.toLowerCase().trim();
780
+ }
781
+ __name(normalizeDomainName, "normalizeDomainName");
782
+ function getHostname(value) {
783
+ const trimmed = value.toLowerCase().trim();
784
+ if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
785
+ try {
786
+ return new URL(trimmed).hostname;
787
+ } catch {
788
+ return trimmed.split(":")[0] ?? trimmed;
789
+ }
790
+ }
791
+ return trimmed.split(":")[0] ?? trimmed;
792
+ }
793
+ __name(getHostname, "getHostname");
794
+ function isOriginAllowedWithoutCheck(originOrHost, customAllowedOrigins) {
795
+ const list = customAllowedOrigins ?? ALLOWED_ORIGINS_WITHOUT_DOMAIN_CHECK;
796
+ const normalized = originOrHost.toLowerCase().trim().replace(/\/+$/, "");
797
+ const hostname = getHostname(originOrHost);
798
+ return list.some((allowed) => {
799
+ const a = allowed.toLowerCase().trim().replace(/\/+$/, "");
800
+ const aHostname = getHostname(a);
801
+ if (normalized === a || hostname === aHostname) return true;
802
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
803
+ return aHostname === "localhost" || aHostname === "127.0.0.1";
804
+ }
805
+ return false;
806
+ });
807
+ }
808
+ __name(isOriginAllowedWithoutCheck, "isOriginAllowedWithoutCheck");
809
+ function isDomainAllowed(currentOriginOrHost, domainName, customAllowedOrigins) {
810
+ const origin = currentOriginOrHost ?? "";
811
+ if (!origin || origin === "null") return true;
812
+ if (isOriginAllowedWithoutCheck(origin, customAllowedOrigins)) {
813
+ return true;
814
+ }
815
+ const current = normalizeHost(
816
+ origin.startsWith("http") ? new URL(origin).host : origin
817
+ );
818
+ const allowed = normalizeDomainName(domainName);
819
+ if (current === allowed) return true;
820
+ const currentHostname = current.split(":")[0] ?? current;
821
+ if (currentHostname === allowed) return true;
822
+ if (currentHostname.endsWith("." + allowed)) return true;
823
+ return false;
824
+ }
825
+ __name(isDomainAllowed, "isDomainAllowed");
826
+
827
+ // src/utils/visitorId.ts
828
+ import { v4 as uuidv42 } from "uuid";
829
+ var STORAGE_PREFIX = "ibtiChatbot@visitorId";
830
+ function storageKey(apiURL, publicHash) {
831
+ const base = apiURL.replace(/\/+$/, "");
832
+ return `${STORAGE_PREFIX}:${base}:${publicHash}`;
833
+ }
834
+ __name(storageKey, "storageKey");
835
+ function getOrCreateVisitorId(apiURL, publicHash) {
836
+ if (typeof window === "undefined") {
837
+ return uuidv42();
838
+ }
839
+ const key = storageKey(apiURL, publicHash);
840
+ const existing = localStorage.getItem(key);
841
+ if (existing) {
842
+ return existing;
843
+ }
844
+ const newId = uuidv42();
845
+ localStorage.setItem(key, newId);
846
+ return newId;
847
+ }
848
+ __name(getOrCreateVisitorId, "getOrCreateVisitorId");
849
+
850
+ // src/contexts/Chatbot/provider.tsx
665
851
  var ChatbotProvider = /* @__PURE__ */ __name(({
666
852
  locale,
667
853
  children,
668
854
  apiURL,
855
+ publicHash,
669
856
  theme,
670
857
  isOpen = false,
671
- texts
858
+ texts,
859
+ allowedOriginsWithoutCheck,
860
+ skipDomainCheck,
861
+ colors: customColors,
862
+ icon: customIcon
672
863
  }) => {
864
+ const [initializationStatus, setInitializationStatus] = useState3(publicHash ? "loading" : "ready");
865
+ const [chatbot, setChatbot] = useState3();
866
+ const [domainAllowed, setDomainAllowed] = useState3(!publicHash);
867
+ const [visitorId, setVisitorId] = useState3(
868
+ () => publicHash ? void 0 : void 0
869
+ );
870
+ const isReady = initializationStatus === "ready" && (domainAllowed || !publicHash);
871
+ useEffect2(() => {
872
+ if (isReady && skipDomainCheck && typeof window !== "undefined" && window.self !== window.top) {
873
+ try {
874
+ window.parent.postMessage({ type: "IBTI_CHATBOT_READY" }, "*");
875
+ } catch {
876
+ }
877
+ }
878
+ }, [isReady, skipDomainCheck]);
673
879
  const {
674
880
  makeQuestion,
675
881
  chatMessages,
@@ -680,13 +886,26 @@ var ChatbotProvider = /* @__PURE__ */ __name(({
680
886
  suggestedQuestions
681
887
  } = useChatbotMessages_default({
682
888
  apiURL,
683
- locale
889
+ locale,
890
+ publicHash: isReady ? publicHash : void 0,
891
+ visitorId: isReady ? visitorId : void 0
684
892
  });
685
893
  const [opened, setOpened] = useState3(isOpen);
686
894
  const scrollRef = useRef2(null);
687
895
  useEffect2(() => {
688
896
  setOpened(isOpen);
689
897
  }, [isOpen]);
898
+ useEffect2(() => {
899
+ if (skipDomainCheck && typeof window !== "undefined" && window.self !== window.top) {
900
+ try {
901
+ window.parent.postMessage(
902
+ { type: "IBTI_CHATBOT_OPEN_STATE", isOpen: opened },
903
+ "*"
904
+ );
905
+ } catch {
906
+ }
907
+ }
908
+ }, [opened, skipDomainCheck]);
690
909
  const openedToggle = /* @__PURE__ */ __name(() => {
691
910
  setOpened((state) => !state);
692
911
  }, "openedToggle");
@@ -696,6 +915,59 @@ var ChatbotProvider = /* @__PURE__ */ __name(({
696
915
  if (loading) return "loading";
697
916
  return "online";
698
917
  }, [writing, loading, error, apiConnectionError]);
918
+ useEffect2(() => {
919
+ if (!publicHash) {
920
+ setInitializationStatus("ready");
921
+ setDomainAllowed(true);
922
+ return;
923
+ }
924
+ let cancelled = false;
925
+ const bootstrap = /* @__PURE__ */ __name(async () => {
926
+ try {
927
+ const data = await getChatbotByPublicHash(apiURL, publicHash);
928
+ if (cancelled) return;
929
+ if (skipDomainCheck) {
930
+ setChatbot(data);
931
+ setDomainAllowed(true);
932
+ const vid = getOrCreateVisitorId(apiURL, publicHash);
933
+ setVisitorId(vid);
934
+ setInitializationStatus("ready");
935
+ return;
936
+ }
937
+ let currentOrigin = typeof window !== "undefined" ? window.location.origin : "";
938
+ if (typeof window !== "undefined" && (currentOrigin === "null" || currentOrigin === "" || currentOrigin == null)) {
939
+ setChatbot(data);
940
+ setDomainAllowed(true);
941
+ const vid = getOrCreateVisitorId(apiURL, publicHash);
942
+ setVisitorId(vid);
943
+ setInitializationStatus("ready");
944
+ return;
945
+ }
946
+ const allowed = isDomainAllowed(
947
+ currentOrigin,
948
+ data.domain.name,
949
+ allowedOriginsWithoutCheck
950
+ );
951
+ setChatbot(data);
952
+ setDomainAllowed(allowed);
953
+ if (allowed) {
954
+ const vid = getOrCreateVisitorId(apiURL, publicHash);
955
+ setVisitorId(vid);
956
+ setInitializationStatus("ready");
957
+ } else {
958
+ setInitializationStatus("domain_not_allowed");
959
+ }
960
+ } catch (err) {
961
+ if (cancelled) return;
962
+ setInitializationStatus("error");
963
+ setDomainAllowed(false);
964
+ }
965
+ }, "bootstrap");
966
+ bootstrap();
967
+ return () => {
968
+ cancelled = true;
969
+ };
970
+ }, [apiURL, publicHash, skipDomainCheck, allowedOriginsWithoutCheck]);
699
971
  return /* @__PURE__ */ React3.createElement(
700
972
  ChatbotContext.Provider,
701
973
  {
@@ -713,10 +985,16 @@ var ChatbotProvider = /* @__PURE__ */ __name(({
713
985
  suggestedQuestions,
714
986
  apiURL,
715
987
  texts,
716
- theme
988
+ theme,
989
+ icon: customIcon,
990
+ publicHash,
991
+ visitorId,
992
+ domainAllowed,
993
+ initializationStatus,
994
+ chatbot
717
995
  }
718
996
  },
719
- /* @__PURE__ */ React3.createElement(ThemeProvider, { theme: themes[theme] }, children)
997
+ /* @__PURE__ */ React3.createElement(ThemeProvider, { theme: mergeThemeWithCustomColors(themes[theme], customColors) }, children)
720
998
  );
721
999
  }, "ChatbotProvider");
722
1000
 
@@ -959,7 +1237,7 @@ var ProfileImage = styled2.span`
959
1237
  }
960
1238
  `;
961
1239
  var Wrapper2 = styled2.div`
962
- background: ${(props) => props.theme.colors.layers[2].background};
1240
+ background: ${(props) => props.theme.colors.custom?.headerBackground ?? props.theme.colors.layers[2].background};
963
1241
  border: 1px solid ${(props) => props.theme.colors.layers[2].border};
964
1242
  display: flex;
965
1243
  padding: ${(props) => props.theme.spacing.components.small};
@@ -978,14 +1256,49 @@ var Wrapper2 = styled2.div`
978
1256
 
979
1257
  // src/components/BotIcon/index.tsx
980
1258
  import React4 from "react";
1259
+
1260
+ // src/components/BotIcon/embedIcons.tsx
1261
+ import {
1262
+ Bot,
1263
+ MessageCircle,
1264
+ MessageSquare,
1265
+ MessageSquarePlus,
1266
+ MessageSquareDashed,
1267
+ Sparkles,
1268
+ Headphones,
1269
+ HelpCircle
1270
+ } from "lucide-react";
1271
+ var EMBED_ICON_MAP = {
1272
+ Bot,
1273
+ MessageCircle,
1274
+ MessageSquare,
1275
+ MessageSquarePlus,
1276
+ MessageSquareDashed,
1277
+ Sparkles,
1278
+ Headphones,
1279
+ HelpCircle
1280
+ };
1281
+ function getEmbedIconComponent(name) {
1282
+ if (!name) return null;
1283
+ return EMBED_ICON_MAP[name] ?? null;
1284
+ }
1285
+ __name(getEmbedIconComponent, "getEmbedIconComponent");
1286
+
1287
+ // src/components/BotIcon/index.tsx
1288
+ var DefaultBotSvg = /* @__PURE__ */ __name(() => /* @__PURE__ */ React4.createElement("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em" }, /* @__PURE__ */ React4.createElement(
1289
+ "path",
1290
+ {
1291
+ d: "M17.7530511,13.999921 C18.9956918,13.999921 20.0030511,15.0072804 20.0030511,16.249921 L20.0030511,17.1550008 C20.0030511,18.2486786 19.5255957,19.2878579 18.6957793,20.0002733 C17.1303315,21.344244 14.8899962,22.0010712 12,22.0010712 C9.11050247,22.0010712 6.87168436,21.3444691 5.30881727,20.0007885 C4.48019625,19.2883988 4.00354153,18.2500002 4.00354153,17.1572408 L4.00354153,16.249921 C4.00354153,15.0072804 5.01090084,13.999921 6.25354153,13.999921 L17.7530511,13.999921 Z M11.8985607,2.00734093 L12.0003312,2.00049432 C12.380027,2.00049432 12.6938222,2.2826482 12.7434846,2.64872376 L12.7503312,2.75049432 L12.7495415,3.49949432 L16.25,3.5 C17.4926407,3.5 18.5,4.50735931 18.5,5.75 L18.5,10.254591 C18.5,11.4972317 17.4926407,12.504591 16.25,12.504591 L7.75,12.504591 C6.50735931,12.504591 5.5,11.4972317 5.5,10.254591 L5.5,5.75 C5.5,4.50735931 6.50735931,3.5 7.75,3.5 L11.2495415,3.49949432 L11.2503312,2.75049432 C11.2503312,2.37079855 11.5324851,2.05700336 11.8985607,2.00734093 L12.0003312,2.00049432 L11.8985607,2.00734093 Z M9.74928905,6.5 C9.05932576,6.5 8.5,7.05932576 8.5,7.74928905 C8.5,8.43925235 9.05932576,8.99857811 9.74928905,8.99857811 C10.4392523,8.99857811 10.9985781,8.43925235 10.9985781,7.74928905 C10.9985781,7.05932576 10.4392523,6.5 9.74928905,6.5 Z M14.2420255,6.5 C13.5520622,6.5 12.9927364,7.05932576 12.9927364,7.74928905 C12.9927364,8.43925235 13.5520622,8.99857811 14.2420255,8.99857811 C14.9319888,8.99857811 15.4913145,8.43925235 15.4913145,7.74928905 C15.4913145,7.05932576 14.9319888,6.5 14.2420255,6.5 Z",
1292
+ fill: "currentColor"
1293
+ }
1294
+ )), "DefaultBotSvg");
981
1295
  var BotIcon = /* @__PURE__ */ __name(() => {
982
- return /* @__PURE__ */ React4.createElement("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em" }, /* @__PURE__ */ React4.createElement(
983
- "path",
984
- {
985
- d: "M17.7530511,13.999921 C18.9956918,13.999921 20.0030511,15.0072804 20.0030511,16.249921 L20.0030511,17.1550008 C20.0030511,18.2486786 19.5255957,19.2878579 18.6957793,20.0002733 C17.1303315,21.344244 14.8899962,22.0010712 12,22.0010712 C9.11050247,22.0010712 6.87168436,21.3444691 5.30881727,20.0007885 C4.48019625,19.2883988 4.00354153,18.2500002 4.00354153,17.1572408 L4.00354153,16.249921 C4.00354153,15.0072804 5.01090084,13.999921 6.25354153,13.999921 L17.7530511,13.999921 Z M11.8985607,2.00734093 L12.0003312,2.00049432 C12.380027,2.00049432 12.6938222,2.2826482 12.7434846,2.64872376 L12.7503312,2.75049432 L12.7495415,3.49949432 L16.25,3.5 C17.4926407,3.5 18.5,4.50735931 18.5,5.75 L18.5,10.254591 C18.5,11.4972317 17.4926407,12.504591 16.25,12.504591 L7.75,12.504591 C6.50735931,12.504591 5.5,11.4972317 5.5,10.254591 L5.5,5.75 C5.5,4.50735931 6.50735931,3.5 7.75,3.5 L11.2495415,3.49949432 L11.2503312,2.75049432 C11.2503312,2.37079855 11.5324851,2.05700336 11.8985607,2.00734093 L12.0003312,2.00049432 L11.8985607,2.00734093 Z M9.74928905,6.5 C9.05932576,6.5 8.5,7.05932576 8.5,7.74928905 C8.5,8.43925235 9.05932576,8.99857811 9.74928905,8.99857811 C10.4392523,8.99857811 10.9985781,8.43925235 10.9985781,7.74928905 C10.9985781,7.05932576 10.4392523,6.5 9.74928905,6.5 Z M14.2420255,6.5 C13.5520622,6.5 12.9927364,7.05932576 12.9927364,7.74928905 C12.9927364,8.43925235 13.5520622,8.99857811 14.2420255,8.99857811 C14.9319888,8.99857811 15.4913145,8.43925235 15.4913145,7.74928905 C15.4913145,7.05932576 14.9319888,6.5 14.2420255,6.5 Z",
986
- fill: "currentColor"
987
- }
988
- ));
1296
+ const { icon: iconName } = useChatbot_default();
1297
+ const IconComponent = getEmbedIconComponent(iconName);
1298
+ if (IconComponent) {
1299
+ return /* @__PURE__ */ React4.createElement(IconComponent, { size: 20, strokeWidth: 1.8 });
1300
+ }
1301
+ return /* @__PURE__ */ React4.createElement(DefaultBotSvg, null);
989
1302
  }, "BotIcon");
990
1303
 
991
1304
  // src/hooks/useChatbotTexts.ts
@@ -1306,7 +1619,7 @@ var Balloon = styled5.div`
1306
1619
  `;
1307
1620
  case "sent":
1308
1621
  return css2`
1309
- background: ${(props) => props.theme.colors.palette.primary.normal};
1622
+ background: ${(props) => props.theme.colors.custom?.userBalloonColor ?? props.theme.colors.palette.primary.normal};
1310
1623
  color: white;
1311
1624
  border-top-right-radius: 0;
1312
1625
  text-align: left;
@@ -1314,18 +1627,27 @@ var Balloon = styled5.div`
1314
1627
  &::after {
1315
1628
  border-width: 0px 0px 10px 10px;
1316
1629
  border-color: transparent transparent transparent
1317
- ${(props) => props.theme.colors.palette.primary.normal};
1630
+ ${(props) => props.theme.colors.custom?.userBalloonColor ?? props.theme.colors.palette.primary.normal};
1318
1631
  top: 0;
1319
1632
  right: -10px;
1320
1633
  }
1321
1634
  `;
1322
1635
  }
1323
1636
  }}
1324
- // Markdown styles
1325
-
1637
+
1326
1638
  display: flex;
1327
- flex-direction: column;
1328
- gap: 1.3em;
1639
+ ${({ $error }) => $error ? css2`
1640
+ flex-direction: row;
1641
+ align-items: center;
1642
+ gap: ${(props) => props.theme.spacing.components.small};
1643
+
1644
+ & > *:first-of-type {
1645
+ flex-shrink: 0;
1646
+ }
1647
+ ` : css2`
1648
+ flex-direction: column;
1649
+ gap: 1.3em;
1650
+ `}
1329
1651
 
1330
1652
  p {
1331
1653
  margin: 0;
@@ -1955,10 +2277,20 @@ var SuggestedQuestions_default = SuggestedQuestions;
1955
2277
 
1956
2278
  // src/components/ChatbotBody/index.tsx
1957
2279
  var ChatbotBody = /* @__PURE__ */ __name(({}) => {
1958
- const { chatMessages, suggestedQuestions, loading } = useChatbot();
2280
+ const {
2281
+ chatMessages,
2282
+ suggestedQuestions,
2283
+ loading,
2284
+ initializationStatus,
2285
+ domainAllowed
2286
+ } = useChatbot();
2287
+ const i18n2 = useI18n();
1959
2288
  const scrollableElementRef = useRef5(null);
1960
2289
  const { scrollProgress, scrollToEnd } = useElementScroll(scrollableElementRef);
1961
2290
  const { dateUTC } = useUpdatedTime({ intervalInSeconds: 15 });
2291
+ const showDomainError = !domainAllowed && initializationStatus === "domain_not_allowed";
2292
+ const showInitError = initializationStatus === "error";
2293
+ const showLoading = initializationStatus === "loading";
1962
2294
  useEffect6(() => {
1963
2295
  if (scrollProgress > 85) scrollToEnd();
1964
2296
  document.addEventListener(
@@ -1977,6 +2309,26 @@ var ChatbotBody = /* @__PURE__ */ __name(({}) => {
1977
2309
  scrollToEnd();
1978
2310
  }
1979
2311
  }, [loading, scrollToEnd]);
2312
+ if (showLoading) {
2313
+ return /* @__PURE__ */ React10.createElement(Wrapper4, { ref: scrollableElementRef }, /* @__PURE__ */ React10.createElement(MessagesList, null, /* @__PURE__ */ React10.createElement(WritingIndicator_default, null)));
2314
+ }
2315
+ if (showDomainError || showInitError) {
2316
+ const errorMessage = showDomainError ? i18n2.ERRORS.DOMAIN_NOT_ALLOWED ?? "O chatbot n\xE3o est\xE1 dispon\xEDvel neste dom\xEDnio." : i18n2.ERRORS.CHATBOT_NOT_FOUND ?? "Chatbot n\xE3o encontrado. Verifique a configura\xE7\xE3o.";
2317
+ return /* @__PURE__ */ React10.createElement(Wrapper4, { ref: scrollableElementRef }, /* @__PURE__ */ React10.createElement(MessagesList, null, /* @__PURE__ */ React10.createElement(
2318
+ MessageBalloon_default,
2319
+ {
2320
+ key: "init-error",
2321
+ data: {
2322
+ id: "init-error",
2323
+ timestamp: (/* @__PURE__ */ new Date()).toUTCString(),
2324
+ role: "assistant",
2325
+ content: errorMessage,
2326
+ error: true
2327
+ },
2328
+ currentDateUTC: dateUTC
2329
+ }
2330
+ )));
2331
+ }
1980
2332
  return /* @__PURE__ */ React10.createElement(Wrapper4, { ref: scrollableElementRef }, /* @__PURE__ */ React10.createElement(MessagesList, null, chatMessages.map((msgData) => /* @__PURE__ */ React10.createElement(
1981
2333
  MessageBalloon_default,
1982
2334
  {
@@ -2353,11 +2705,16 @@ var Title2 = styled10.span`
2353
2705
  `;
2354
2706
  var Box = styled10(CardBase)`
2355
2707
  width: 100%;
2708
+ max-width: 100%;
2356
2709
  height: max-content;
2710
+ max-height: 100%;
2711
+ min-height: 0;
2712
+ overflow-y: auto;
2357
2713
  padding: ${(props) => props.theme.spacing.components.medium};
2358
2714
  transition: ${(props) => props.theme.transitionDurations.default};
2359
2715
  font-family: 'Montserrat', sans-serif !important;
2360
2716
  font-weight: 600 !important;
2717
+ box-sizing: border-box;
2361
2718
 
2362
2719
  * {
2363
2720
  font-family: 'Montserrat', sans-serif !important;
@@ -2378,6 +2735,8 @@ var Wrapper9 = styled10.div`
2378
2735
  align-items: center;
2379
2736
  justify-content: center;
2380
2737
  padding: ${(props) => props.theme.spacing.components.medium};
2738
+ box-sizing: border-box;
2739
+ overflow: auto;
2381
2740
  transition: ${(props) => props.theme.transitionDurations.default};
2382
2741
 
2383
2742
  ${(props) => props.$opened ? css5`
@@ -2448,7 +2807,7 @@ var ChatUserFeedbackRating = /* @__PURE__ */ __name(({
2448
2807
  // src/contexts/Chatbot/useChatFeedbackBox.ts
2449
2808
  import { useState as useState9 } from "react";
2450
2809
  var useChatFeedbackBox = /* @__PURE__ */ __name(() => {
2451
- const { chatMessages, apiURL, locale } = useChatbot_default();
2810
+ const { chatMessages, apiURL, locale, publicHash, visitorId } = useChatbot_default();
2452
2811
  const [rated, setRated] = useState9(false);
2453
2812
  const [opened, setOpened] = useState9(false);
2454
2813
  const [loading, setLoading] = useState9(false);
@@ -2467,6 +2826,8 @@ var useChatFeedbackBox = /* @__PURE__ */ __name(() => {
2467
2826
  setLoading(true);
2468
2827
  await sendUserChatFeedback({
2469
2828
  apiURL,
2829
+ publicHash,
2830
+ visitorId,
2470
2831
  chatContext: chatMessages.map((item) => ({
2471
2832
  role: item.role,
2472
2833
  content: item.content