@magicfeedback/popup-sdk 1.14.0 → 1.16.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.js CHANGED
@@ -256,7 +256,8 @@ var init_labels = __esm({
256
256
  closeAria: "Close popup",
257
257
  loadingAria: "Loading survey",
258
258
  errorRequired: "Please answer the required question to continue.",
259
- errorSubmit: "An error occurred while submitting. Please try again or close the popup."
259
+ errorSubmit: "An error occurred while submitting. Please try again or close the popup.",
260
+ errorLoad: "We couldn't load this survey."
260
261
  };
261
262
  ES = {
262
263
  accept: "Enviar",
@@ -270,7 +271,8 @@ var init_labels = __esm({
270
271
  closeAria: "Cerrar ventana emergente",
271
272
  loadingAria: "Cargando encuesta",
272
273
  errorRequired: "Responde la pregunta obligatoria para continuar.",
273
- errorSubmit: "Se produjo un error al enviar. Int\xE9ntalo de nuevo o cierra la ventana."
274
+ errorSubmit: "Se produjo un error al enviar. Int\xE9ntalo de nuevo o cierra la ventana.",
275
+ errorLoad: "No se ha podido cargar la encuesta."
274
276
  };
275
277
  DA = {
276
278
  accept: "Send",
@@ -284,7 +286,8 @@ var init_labels = __esm({
284
286
  closeAria: "Luk pop op",
285
287
  loadingAria: "Indl\xE6ser unders\xF8gelse",
286
288
  errorRequired: "Besvar venligst det obligatoriske sp\xF8rgsm\xE5l for at forts\xE6tte.",
287
- errorSubmit: "Der opstod en fejl under afsendelsen. Pr\xF8v igen, eller luk pop op-vinduet."
289
+ errorSubmit: "Der opstod en fejl under afsendelsen. Pr\xF8v igen, eller luk pop op-vinduet.",
290
+ errorLoad: "Unders\xF8gelsen kunne ikke indl\xE6ses."
288
291
  };
289
292
  NO = {
290
293
  accept: "Send",
@@ -298,7 +301,8 @@ var init_labels = __esm({
298
301
  closeAria: "Lukk popup",
299
302
  loadingAria: "Laster unders\xF8kelse",
300
303
  errorRequired: "Svar p\xE5 det obligatoriske sp\xF8rsm\xE5let for \xE5 fortsette.",
301
- errorSubmit: "Det oppstod en feil under innsendingen. Pr\xF8v igjen, eller lukk popupen."
304
+ errorSubmit: "Det oppstod en feil under innsendingen. Pr\xF8v igjen, eller lukk popupen.",
305
+ errorLoad: "Unders\xF8kelsen kunne ikke lastes inn."
302
306
  };
303
307
  SV = {
304
308
  accept: "Skicka",
@@ -312,7 +316,8 @@ var init_labels = __esm({
312
316
  closeAria: "St\xE4ng popup",
313
317
  loadingAria: "Laddar unders\xF6kning",
314
318
  errorRequired: "Svara p\xE5 den obligatoriska fr\xE5gan f\xF6r att forts\xE4tta.",
315
- errorSubmit: "Ett fel uppstod vid inskickningen. F\xF6rs\xF6k igen eller st\xE4ng popupen."
319
+ errorSubmit: "Ett fel uppstod vid inskickningen. F\xF6rs\xF6k igen eller st\xE4ng popupen.",
320
+ errorLoad: "Enk\xE4ten kunde inte laddas."
316
321
  };
317
322
  FI = {
318
323
  accept: "L\xE4het\xE4",
@@ -326,7 +331,8 @@ var init_labels = __esm({
326
331
  closeAria: "Sulje ponnahdusikkuna",
327
332
  loadingAria: "Ladataan kysely\xE4",
328
333
  errorRequired: "Vastaa pakolliseen kysymykseen jatkaaksesi.",
329
- errorSubmit: "L\xE4hetyksess\xE4 tapahtui virhe. Yrit\xE4 uudelleen tai sulje ponnahdusikkuna."
334
+ errorSubmit: "L\xE4hetyksess\xE4 tapahtui virhe. Yrit\xE4 uudelleen tai sulje ponnahdusikkuna.",
335
+ errorLoad: "Kysely\xE4 ei voitu ladata."
330
336
  };
331
337
  DE = {
332
338
  accept: "Senden",
@@ -340,7 +346,8 @@ var init_labels = __esm({
340
346
  closeAria: "Popup schlie\xDFen",
341
347
  loadingAria: "Umfrage wird geladen",
342
348
  errorRequired: "Bitte beantworte die Pflichtfrage, um fortzufahren.",
343
- errorSubmit: "Beim Senden ist ein Fehler aufgetreten. Versuche es erneut oder schlie\xDFe das Popup."
349
+ errorSubmit: "Beim Senden ist ein Fehler aufgetreten. Versuche es erneut oder schlie\xDFe das Popup.",
350
+ errorLoad: "Die Umfrage konnte nicht geladen werden."
344
351
  };
345
352
  FR = {
346
353
  accept: "Envoyer",
@@ -354,7 +361,8 @@ var init_labels = __esm({
354
361
  closeAria: "Fermer la fen\xEAtre",
355
362
  loadingAria: "Chargement de l'enqu\xEAte",
356
363
  errorRequired: "Veuillez r\xE9pondre \xE0 la question obligatoire pour continuer.",
357
- errorSubmit: "Une erreur s'est produite lors de l'envoi. R\xE9essayez ou fermez la fen\xEAtre."
364
+ errorSubmit: "Une erreur s'est produite lors de l'envoi. R\xE9essayez ou fermez la fen\xEAtre.",
365
+ errorLoad: "Impossible de charger l'enqu\xEAte."
358
366
  };
359
367
  PT = {
360
368
  accept: "Enviar",
@@ -368,7 +376,8 @@ var init_labels = __esm({
368
376
  closeAria: "Fechar janela",
369
377
  loadingAria: "A carregar question\xE1rio",
370
378
  errorRequired: "Responda \xE0 pergunta obrigat\xF3ria para continuar.",
371
- errorSubmit: "Ocorreu um erro ao enviar. Tente novamente ou feche a janela."
379
+ errorSubmit: "Ocorreu um erro ao enviar. Tente novamente ou feche a janela.",
380
+ errorLoad: "N\xE3o foi poss\xEDvel carregar o inqu\xE9rito."
372
381
  };
373
382
  AR = {
374
383
  accept: "\u0625\u0631\u0633\u0627\u0644",
@@ -382,7 +391,8 @@ var init_labels = __esm({
382
391
  closeAria: "\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0646\u0627\u0641\u0630\u0629 \u0627\u0644\u0645\u0646\u0628\u062B\u0642\u0629",
383
392
  loadingAria: "\u062C\u0627\u0631\u064D \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0627\u0633\u062A\u0628\u064A\u0627\u0646",
384
393
  errorRequired: "\u064A\u0631\u062C\u0649 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0639\u0644\u0649 \u0627\u0644\u0633\u0624\u0627\u0644 \u0627\u0644\u0645\u0637\u0644\u0648\u0628 \u0644\u0644\u0645\u062A\u0627\u0628\u0639\u0629.",
385
- errorSubmit: "\u062D\u062F\u062B \u062E\u0637\u0623 \u0623\u062B\u0646\u0627\u0621 \u0627\u0644\u0625\u0631\u0633\u0627\u0644. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649 \u0623\u0648 \u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0646\u0627\u0641\u0630\u0629."
394
+ errorSubmit: "\u062D\u062F\u062B \u062E\u0637\u0623 \u0623\u062B\u0646\u0627\u0621 \u0627\u0644\u0625\u0631\u0633\u0627\u0644. \u064A\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649 \u0623\u0648 \u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0646\u0627\u0641\u0630\u0629.",
395
+ errorLoad: "\u062A\u0639\u0630\u0651\u0631 \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0627\u0633\u062A\u0628\u064A\u0627\u0646."
386
396
  };
387
397
  BN = {
388
398
  accept: "\u09AA\u09BE\u09A0\u09BE\u09A8",
@@ -396,7 +406,8 @@ var init_labels = __esm({
396
406
  closeAria: "\u09AA\u09AA\u0986\u09AA \u09AC\u09A8\u09CD\u09A7 \u0995\u09B0\u09C1\u09A8",
397
407
  loadingAria: "\u099C\u09B0\u09BF\u09AA \u09B2\u09CB\u09A1 \u09B9\u099A\u09CD\u099B\u09C7",
398
408
  errorRequired: "\u099A\u09BE\u09B2\u09BF\u09AF\u09BC\u09C7 \u09AF\u09C7\u09A4\u09C7 \u0986\u09AC\u09B6\u09CD\u09AF\u0995 \u09AA\u09CD\u09B0\u09B6\u09CD\u09A8\u09C7\u09B0 \u0989\u09A4\u09CD\u09A4\u09B0 \u09A6\u09BF\u09A8\u0964",
399
- errorSubmit: "\u099C\u09AE\u09BE \u09A6\u09C7\u0993\u09AF\u09BC\u09BE\u09B0 \u09B8\u09AE\u09AF\u09BC \u098F\u0995\u099F\u09BF \u09A4\u09CD\u09B0\u09C1\u099F\u09BF \u0998\u099F\u09C7\u099B\u09C7\u0964 \u0986\u09AC\u09BE\u09B0 \u099A\u09C7\u09B7\u09CD\u099F\u09BE \u0995\u09B0\u09C1\u09A8 \u09AC\u09BE \u09AA\u09AA\u0986\u09AA \u09AC\u09A8\u09CD\u09A7 \u0995\u09B0\u09C1\u09A8\u0964"
409
+ errorSubmit: "\u099C\u09AE\u09BE \u09A6\u09C7\u0993\u09AF\u09BC\u09BE\u09B0 \u09B8\u09AE\u09AF\u09BC \u098F\u0995\u099F\u09BF \u09A4\u09CD\u09B0\u09C1\u099F\u09BF \u0998\u099F\u09C7\u099B\u09C7\u0964 \u0986\u09AC\u09BE\u09B0 \u099A\u09C7\u09B7\u09CD\u099F\u09BE \u0995\u09B0\u09C1\u09A8 \u09AC\u09BE \u09AA\u09AA\u0986\u09AA \u09AC\u09A8\u09CD\u09A7 \u0995\u09B0\u09C1\u09A8\u0964",
410
+ errorLoad: "\u09B8\u09AE\u09C0\u0995\u09CD\u09B7\u09BE\u099F\u09BF \u09B2\u09CB\u09A1 \u0995\u09B0\u09BE \u09AF\u09BE\u09AF\u09BC\u09A8\u09BF\u0964"
400
411
  };
401
412
  ZH_CN = {
402
413
  accept: "\u53D1\u9001",
@@ -410,7 +421,8 @@ var init_labels = __esm({
410
421
  closeAria: "\u5173\u95ED\u5F39\u7A97",
411
422
  loadingAria: "\u6B63\u5728\u52A0\u8F7D\u95EE\u5377",
412
423
  errorRequired: "\u8BF7\u56DE\u7B54\u5FC5\u7B54\u9898\u540E\u7EE7\u7EED\u3002",
413
- errorSubmit: "\u63D0\u4EA4\u65F6\u51FA\u9519\u3002\u8BF7\u91CD\u8BD5\u6216\u5173\u95ED\u5F39\u7A97\u3002"
424
+ errorSubmit: "\u63D0\u4EA4\u65F6\u51FA\u9519\u3002\u8BF7\u91CD\u8BD5\u6216\u5173\u95ED\u5F39\u7A97\u3002",
425
+ errorLoad: "\u65E0\u6CD5\u52A0\u8F7D\u95EE\u5377\u3002"
414
426
  };
415
427
  LABELS = {
416
428
  en: EN,
@@ -5628,30 +5640,35 @@ __export(renderPopup_exports, {
5628
5640
  SURVEY_ALIGNMENT_STYLE_ID: () => SURVEY_ALIGNMENT_STYLE_ID,
5629
5641
  renderPopup: () => renderPopup
5630
5642
  });
5631
- function ensureMagicFeedbackStyles(_popup) {
5632
- const STYLE_ID = "magicfeedback-sdk-styles";
5633
- if (document.getElementById(STYLE_ID)) return;
5634
- const style = document.createElement("style");
5635
- style.id = STYLE_ID;
5636
- style.textContent = style_default;
5637
- document.head.appendChild(style);
5638
- }
5639
- function ensureSurveyAlignmentStyles() {
5640
- if (document.getElementById(SURVEY_ALIGNMENT_STYLE_ID)) return;
5641
- const style = document.createElement("style");
5642
- style.id = SURVEY_ALIGNMENT_STYLE_ID;
5643
- style.textContent = `
5643
+ function ensureStyle(id, css, nonce) {
5644
+ let el = document.getElementById(id);
5645
+ if (el && nonce && el.getAttribute("nonce") !== nonce) {
5646
+ el.remove();
5647
+ el = null;
5648
+ }
5649
+ if (!el) {
5650
+ el = document.createElement("style");
5651
+ el.id = id;
5652
+ if (nonce) el.setAttribute("nonce", nonce);
5653
+ el.textContent = css;
5654
+ document.head.appendChild(el);
5655
+ } else if (el.textContent !== css) {
5656
+ el.textContent = css;
5657
+ }
5658
+ return el;
5659
+ }
5660
+ function ensureMagicFeedbackStyles(nonce) {
5661
+ ensureStyle("magicfeedback-sdk-styles", style_default, nonce);
5662
+ }
5663
+ function ensureSurveyAlignmentStyles(nonce) {
5664
+ ensureStyle(SURVEY_ALIGNMENT_STYLE_ID, `
5644
5665
  .deepdots-popup .magicfeedback-container,
5645
5666
  .deepdots-popup .magicfeedback-form,
5646
5667
  .deepdots-popup .magicfeedback-div { padding-left: 0; padding-right: 0; }
5647
- `;
5648
- document.head.appendChild(style);
5649
- }
5650
- function ensureSpinnerStyles(_popup) {
5651
- if (document.getElementById("deepdots-spinner-styles")) return;
5652
- const style = document.createElement("style");
5653
- style.id = "deepdots-spinner-styles";
5654
- style.textContent = `
5668
+ `, nonce);
5669
+ }
5670
+ function ensureSpinnerStyles(nonce) {
5671
+ ensureStyle("deepdots-spinner-styles", `
5655
5672
  /* El enunciado trae margin-top:20px del CSS de surveys, que sumaba al hueco de la barra
5656
5673
  de progreso y descompensaba la simetr\xEDa. Solo se anula en el primero: entre preguntas
5657
5674
  de la misma p\xE1gina sigue separando. */
@@ -5659,14 +5676,10 @@ function ensureSpinnerStyles(_popup) {
5659
5676
  @keyframes ddspin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
5660
5677
  .mf-spinner { display:flex; justify-content:center; align-items:center; padding:8px 0; }
5661
5678
  .mf-spinner-circle { width:28px; height:28px; border:3px solid #e0e6ed; border-top-color:#1E293B; border-radius:50%; animation: ddspin 0.9s linear infinite; }
5662
- `;
5663
- document.head.appendChild(style);
5664
- }
5665
- function ensureResponsiveStyles(_popup) {
5666
- if (document.getElementById("deepdots-responsive-styles")) return;
5667
- const style = document.createElement("style");
5668
- style.id = "deepdots-responsive-styles";
5669
- style.textContent = `
5679
+ `, nonce);
5680
+ }
5681
+ function ensureResponsiveStyles(nonce) {
5682
+ ensureStyle("deepdots-responsive-styles", `
5670
5683
  /* Responsive adjustments */
5671
5684
  @media (max-width: 640px) {
5672
5685
  .deepdots-popup {
@@ -5699,32 +5712,20 @@ function ensureResponsiveStyles(_popup) {
5699
5712
  overflow-y: auto !important;
5700
5713
  }
5701
5714
  }
5702
- `;
5703
- document.head.appendChild(style);
5715
+ `, nonce);
5704
5716
  }
5705
- function ensureCustomCss(css) {
5717
+ function ensureCustomCss(css, nonce) {
5706
5718
  const STYLE_ID = "deepdots-custom-css";
5707
- const existing = document.getElementById(STYLE_ID);
5708
5719
  if (!css) {
5709
- existing?.remove();
5720
+ document.getElementById(STYLE_ID)?.remove();
5710
5721
  return;
5711
5722
  }
5712
- const el = existing ?? document.createElement("style");
5713
- el.id = STYLE_ID;
5714
- if (el.textContent !== css) el.textContent = css;
5715
- document.head.appendChild(el);
5723
+ document.head.appendChild(ensureStyle(STYLE_ID, css, nonce));
5716
5724
  }
5717
- function ensureFontFace(family, url) {
5725
+ function ensureFontFace(family, url, nonce) {
5718
5726
  const css = buildFontFaceCss(family, url);
5719
5727
  if (!css) return;
5720
- const STYLE_ID = "deepdots-font-face";
5721
- let el = document.getElementById(STYLE_ID);
5722
- if (!el) {
5723
- el = document.createElement("style");
5724
- el.id = STYLE_ID;
5725
- document.head.appendChild(el);
5726
- }
5727
- if (el.textContent !== css) el.textContent = css;
5728
+ ensureStyle("deepdots-font-face", css, nonce);
5728
5729
  }
5729
5730
  function hasFinePointer() {
5730
5731
  return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(pointer: fine)").matches;
@@ -5734,6 +5735,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
5734
5735
  let stylesInjected = false;
5735
5736
  let pageDepth = 0;
5736
5737
  let onStartPage = false;
5738
+ const cspNonce = options?.cspNonce?.trim() || void 0;
5737
5739
  let labels = getLabels(options?.language);
5738
5740
  let actionLabels = resolveActionLabels(actions, options?.language);
5739
5741
  let direction = directionFor(options?.language);
@@ -5785,7 +5787,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
5785
5787
  popup.style.fontFamily = familyValue;
5786
5788
  popup.style.setProperty("--deepdots-font", familyValue);
5787
5789
  popup.classList.add("deepdots-has-font");
5788
- if (font.url) ensureFontFace(font.family, font.url);
5790
+ if (font.url) ensureFontFace(font.family, font.url, cspNonce);
5789
5791
  }
5790
5792
  const header = document.createElement("div");
5791
5793
  header.className = "deepdots-popup-header";
@@ -5834,10 +5836,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
5834
5836
  closeBtn.style.color = theme.textPrimary;
5835
5837
  closeBtn.style.transform = "scale(1)";
5836
5838
  };
5837
- closeBtn.onclick = () => {
5839
+ const closeFromChrome = () => {
5838
5840
  emit("popup_clicked", surveyId, { action: "close_icon" });
5839
5841
  onClose();
5840
5842
  };
5843
+ closeBtn.onclick = closeFromChrome;
5841
5844
  closeBtn.style.flex = "0 0 auto";
5842
5845
  closeBtn.style.marginLeft = "auto";
5843
5846
  header.appendChild(closeBtn);
@@ -5901,11 +5904,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
5901
5904
  }
5902
5905
  progressFollowUp.style.display = p.followup ? "inline-block" : "none";
5903
5906
  }
5904
- ensureMagicFeedbackStyles(popup);
5905
- ensureSurveyAlignmentStyles();
5906
- ensureSpinnerStyles(popup);
5907
- ensureResponsiveStyles(popup);
5908
- ensureCustomCss(options?.surveyCss);
5907
+ ensureMagicFeedbackStyles(cspNonce);
5908
+ ensureSurveyAlignmentStyles(cspNonce);
5909
+ ensureSpinnerStyles(cspNonce);
5910
+ ensureResponsiveStyles(cspNonce);
5911
+ ensureCustomCss(options?.surveyCss, cspNonce);
5909
5912
  const containerContent = document.createElement("div");
5910
5913
  containerContent.id = "dd-content";
5911
5914
  containerContent.className = "deepdots-popup-container-content";
@@ -5945,6 +5948,12 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
5945
5948
  formWrapper.appendChild(spinnerEl);
5946
5949
  formWrapper.appendChild(formHost);
5947
5950
  main.appendChild(formWrapper);
5951
+ const loadErrorEl = document.createElement("div");
5952
+ loadErrorEl.id = "dd-load-error";
5953
+ loadErrorEl.className = "deepdots-load-error";
5954
+ loadErrorEl.setAttribute("role", "alert");
5955
+ loadErrorEl.style.cssText = `display:none; width:100%; text-align:center; padding:24px 0; font-size:15px; color:${theme.textPrimary};`;
5956
+ main.appendChild(loadErrorEl);
5948
5957
  main.appendChild(errorHint);
5949
5958
  const footer = document.createElement("div");
5950
5959
  footer.id = "dd-footer";
@@ -6060,6 +6069,13 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
6060
6069
  justify-content: center;
6061
6070
  text-align: center;
6062
6071
  `;
6072
+ const cancelButton = document.createElement("button");
6073
+ cancelButton.id = "dd-cancel";
6074
+ cancelButton.type = "button";
6075
+ cancelButton.className = "dd-nav-btn";
6076
+ cancelButton.textContent = actionLabels.decline;
6077
+ cancelButton.style.cssText = submitButton.style.cssText;
6078
+ cancelButton.onclick = closeFromChrome;
6063
6079
  submitButton.onclick = () => {
6064
6080
  if (!surveyCompletedEmitted) {
6065
6081
  emit("popup_clicked", surveyId, { action: "manual_send" });
@@ -6073,6 +6089,8 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
6073
6089
  startButton.textContent = actionLabels.start;
6074
6090
  closeButton.textContent = actionLabels.complete;
6075
6091
  submitButton.textContent = actionLabels.accept;
6092
+ cancelButton.textContent = actionLabels.decline;
6093
+ loadErrorEl.textContent = labels.errorLoad;
6076
6094
  closeBtn.setAttribute("aria-label", labels.closeAria);
6077
6095
  spinnerEl.setAttribute("aria-label", labels.loadingAria);
6078
6096
  progressFollowUp.textContent = labels.followUp;
@@ -6083,10 +6101,12 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
6083
6101
  startButton.style.display = "none";
6084
6102
  submitButton.style.display = "none";
6085
6103
  closeButton.style.display = "none";
6104
+ cancelButton.style.display = "none";
6086
6105
  footer.appendChild(submitButton);
6087
6106
  footer.appendChild(backButton);
6088
6107
  footer.appendChild(closeButton);
6089
6108
  footer.appendChild(startButton);
6109
+ footer.appendChild(cancelButton);
6090
6110
  containerContent.appendChild(main);
6091
6111
  footer.style.flex = "0 0 auto";
6092
6112
  containerContent.appendChild(footer);
@@ -6095,6 +6115,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
6095
6115
  startButton.style.display = "none";
6096
6116
  submitButton.style.display = "none";
6097
6117
  closeButton.style.display = "none";
6118
+ cancelButton.style.display = "none";
6098
6119
  backButton.style.width = "";
6099
6120
  startButton.style.width = "";
6100
6121
  submitButton.style.width = "";
@@ -6124,6 +6145,9 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
6124
6145
  case "error":
6125
6146
  setLoading(false);
6126
6147
  break;
6148
+ case "load_error":
6149
+ cancelButton.style.display = "inline-flex";
6150
+ break;
6127
6151
  }
6128
6152
  }
6129
6153
  function updateNavButtons() {
@@ -6145,6 +6169,20 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
6145
6169
  img.style.cssText = "max-width:100%; height:auto; margin:0 auto 16px auto; display:block;";
6146
6170
  });
6147
6171
  }
6172
+ let loadFailed = false;
6173
+ function showLoadError(reason) {
6174
+ sdkWarn("[Deepdots] survey failed to load:", reason);
6175
+ if (loadFailed) return;
6176
+ loadFailed = true;
6177
+ progressEl.style.display = "none";
6178
+ formWrapper.style.display = "none";
6179
+ errorHint.style.display = "none";
6180
+ loadErrorEl.textContent = labels.errorLoad;
6181
+ loadErrorEl.style.display = "block";
6182
+ setLoading(false);
6183
+ updateButtons("load_error");
6184
+ reveal();
6185
+ }
6148
6186
  popup.appendChild(header);
6149
6187
  popup.appendChild(progressEl);
6150
6188
  popup.appendChild(containerContent);
@@ -6209,7 +6247,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
6209
6247
  try {
6210
6248
  if (!import_native.default || typeof import_native.default.form !== "function") {
6211
6249
  sdkWarn("[MagicFeedback] form() no disponible. Fallback manual.");
6212
- setLoading(false);
6250
+ showLoadError("form() not available");
6213
6251
  return;
6214
6252
  }
6215
6253
  import_native.default.init(
@@ -6235,7 +6273,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
6235
6273
  // quitado el `inert`. La apertura va aparte (ver autofocusOnOpen).
6236
6274
  autofocus: "navigation"
6237
6275
  };
6238
- generateOptions.onLoadedEvent = ({ formData, lang }) => {
6276
+ generateOptions.onLoadedEvent = ({ formData, lang, error }) => {
6277
+ if (error) {
6278
+ showLoadError(error);
6279
+ return;
6280
+ }
6239
6281
  const shownLang = typeof lang === "string" && lang.trim().length > 0 ? lang : void 0;
6240
6282
  const surveyLang = shownLang ?? (Array.isArray(formData?.lang) ? formData.lang.find((l) => typeof l === "string" && l.trim().length > 0) : void 0);
6241
6283
  if (surveyLang) applyLabels(surveyLang);
@@ -6334,11 +6376,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
6334
6376
  };
6335
6377
  formInstance.generate(formDivId, generateOptions).catch((err) => {
6336
6378
  sdkError("[MagicFeedback] Error generating form:", err);
6337
- setLoading(false);
6379
+ showLoadError(err);
6338
6380
  });
6339
6381
  } catch (e) {
6340
6382
  sdkError("[MagicFeedback] Exception initializing form:", e);
6341
- setLoading(false);
6383
+ showLoadError(e);
6342
6384
  }
6343
6385
  }
6344
6386
  var import_native, SURVEY_ALIGNMENT_STYLE_ID;
@@ -6674,12 +6716,14 @@ function buildSurveyHtml(opts) {
6674
6716
  const startLabel = jsonForScript(initActionLabels.start);
6675
6717
  const completeLabel = jsonForScript(initActionLabels.complete);
6676
6718
  const submitLabel = jsonForScript(initActionLabels.accept);
6719
+ const cancelLabel = jsonForScript(initActionLabels.decline);
6677
6720
  const labelsTableJson = jsonForScript(LABELS);
6678
6721
  const actionOverridesJson = jsonForScript({
6679
6722
  accept: trimmedOrNull(opts.actions?.accept?.label),
6680
6723
  start: trimmedOrNull(opts.actions?.start?.label),
6681
6724
  complete: trimmedOrNull(opts.actions?.complete?.label),
6682
- back: trimmedOrNull(opts.actions?.back?.label)
6725
+ back: trimmedOrNull(opts.actions?.back?.label),
6726
+ decline: trimmedOrNull(opts.actions?.decline?.label)
6683
6727
  });
6684
6728
  const initLangJson = jsonForScript(opts.language ?? "");
6685
6729
  const initDirection = directionFor(opts.language);
@@ -6741,6 +6785,7 @@ body.dd-ready #dd-popup{visibility:visible}
6741
6785
  #dd-logo{max-height:40px;max-width:100%;object-fit:contain;display:block;margin:12px 0 0 0}
6742
6786
  .deepdots-error-hint{display:none;margin:12px 0 0 0;padding:10px 12px;border-radius:6px;background:#FEF3C7;color:#92400E;border:1px solid #FCD34D;font-size:13px}
6743
6787
  /* Pantalla final: el HTML del editor de la plataforma (imagen + texto), centrado. */
6788
+ .deepdots-load-error{display:none;width:100%;text-align:center;padding:24px 0;font-size:15px;color:${textPrimary}}
6744
6789
  .deepdots-success{display:none;width:100%;text-align:center;padding:24px 0;color:${textPrimary}}
6745
6790
  .deepdots-success img{max-width:100%;height:auto;margin:0 auto 16px auto;display:block}
6746
6791
  .deepdots-success p{margin:0;font-size:16px;font-weight:600;line-height:1.4}
@@ -6748,7 +6793,7 @@ body.dd-ready #dd-popup{visibility:visible}
6748
6793
  .dd-nav-btn{display:none;border:none;min-height:44px;padding:12px 24px;border-radius:999px;cursor:pointer;font-size:15px;font-weight:600;align-items:center;justify-content:center;text-align:center}
6749
6794
  /* Secundario como bot\xF3n de texto: sin borde ni fondo, para que el primario sea la \xFAnica CTA. */
6750
6795
  #dd-back{background:transparent;color:${textMuted};border:none}
6751
- #dd-start,#dd-complete,#dd-submit{background:#1E293B;color:#fff;border:none}
6796
+ #dd-start,#dd-complete,#dd-submit,#dd-cancel{background:#1E293B;color:#fff;border:none}
6752
6797
  #dd-submit:disabled,#dd-start:disabled,#dd-back:disabled,#dd-complete:disabled{opacity:0.6;cursor:not-allowed}
6753
6798
  .mf-spinner{display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}
6754
6799
  .mf-spinner-circle{width:28px;height:28px;border:3px solid #e0e6ed;border-top-color:#1E293B;border-radius:50%;animation:ddspin 0.9s linear infinite}
@@ -6789,6 +6834,7 @@ ${customCss}
6789
6834
  <div class="mf-spinner" id="dd-spinner" role="status" aria-label="${initLabels.loadingAria}"><div class="mf-spinner-circle"></div></div>
6790
6835
  <div id="mf"></div>
6791
6836
  </div>
6837
+ <div class="deepdots-load-error" id="dd-load-error" role="alert"></div>
6792
6838
  <div class="deepdots-error-hint" id="dd-error" role="alert" aria-live="polite"></div>
6793
6839
  </div>
6794
6840
  <div class="deepdots-popup-footer" id="dd-footer">
@@ -6796,6 +6842,7 @@ ${customCss}
6796
6842
  <button type="button" class="dd-nav-btn" id="dd-back"></button>
6797
6843
  <button type="button" class="dd-nav-btn" id="dd-complete"></button>
6798
6844
  <button type="button" class="dd-nav-btn" id="dd-start"></button>
6845
+ <button type="button" class="dd-nav-btn" id="dd-cancel"></button>
6799
6846
  </div>
6800
6847
  </div>
6801
6848
  </div>
@@ -6818,6 +6865,8 @@ ${customCss}
6818
6865
  var startBtn=document.getElementById('dd-start');
6819
6866
  var completeBtn=document.getElementById('dd-complete');
6820
6867
  var submitBtn=document.getElementById('dd-submit');
6868
+ var cancelBtn=document.getElementById('dd-cancel');
6869
+ var loadErrorEl=document.getElementById('dd-load-error');
6821
6870
  var titleEl=document.getElementById('dd-title');
6822
6871
  var progressEl=document.getElementById('dd-progress');
6823
6872
  var progressLabel=document.getElementById('dd-progress-label');
@@ -6847,6 +6896,8 @@ ${customCss}
6847
6896
  startBtn.textContent=DD_ACTION_OVERRIDES.start||next.start;
6848
6897
  completeBtn.textContent=DD_ACTION_OVERRIDES.complete||next.complete;
6849
6898
  submitBtn.textContent=DD_ACTION_OVERRIDES.accept||next.accept;
6899
+ cancelBtn.textContent=DD_ACTION_OVERRIDES.decline||next.decline;
6900
+ loadErrorEl.textContent=next.errorLoad;
6850
6901
  if(closeEl){ closeEl.setAttribute('aria-label',next.closeAria); }
6851
6902
  if(spinner){ spinner.setAttribute('aria-label',next.loadingAria); }
6852
6903
  progressFollowUp.textContent=next.followUp;
@@ -6901,6 +6952,7 @@ ${customCss}
6901
6952
  startBtn.textContent=${startLabel};
6902
6953
  completeBtn.textContent=${completeLabel};
6903
6954
  submitBtn.textContent=${submitLabel};
6955
+ cancelBtn.textContent=${cancelLabel};
6904
6956
 
6905
6957
  closeEl.onclick=function(){ emitJSON('popup_close'); };
6906
6958
  backBtn.onclick=function(){ if(window.DeepdotsForm && window.DeepdotsForm.back){ window.DeepdotsForm.back(); } };
@@ -6912,6 +6964,8 @@ ${customCss}
6912
6964
  }
6913
6965
  };
6914
6966
  completeBtn.onclick=function(){ emitJSON('popup_close'); };
6967
+ // \xDAnica acci\xF3n del survey que no carga: cierra igual que la X.
6968
+ cancelBtn.onclick=function(){ emitJSON('popup_close'); };
6915
6969
  var surveyCompletedEmitted=false;
6916
6970
  submitBtn.onclick=function(){
6917
6971
  if(surveyCompletedEmitted) return;
@@ -6953,6 +7007,7 @@ ${customCss}
6953
7007
  startBtn.style.display='none';
6954
7008
  submitBtn.style.display='none';
6955
7009
  completeBtn.style.display='none';
7010
+ cancelBtn.style.display='none';
6956
7011
  backBtn.style.width='';
6957
7012
  startBtn.style.width='';
6958
7013
  submitBtn.style.width='';
@@ -6968,6 +7023,8 @@ ${customCss}
6968
7023
  } else if(state==='completed'){
6969
7024
  completeBtn.style.display='inline-flex';
6970
7025
  completeBtn.style.width='100%';
7026
+ } else if(state==='load_error'){
7027
+ cancelBtn.style.display='inline-flex';
6971
7028
  }
6972
7029
  }
6973
7030
 
@@ -7014,6 +7071,26 @@ ${REVEAL_JS}
7014
7071
  }
7015
7072
  setLoading(true);
7016
7073
 
7074
+ // Survey que no ha cargado (406 de /info, sin red, bundle del CDN ca\xEDdo). Antes se emit\xEDa
7075
+ // \`loaded\` y se pintaba un Send sobre un survey vac\xEDo. Espejo de KMP: aviso traducido, la X
7076
+ // sigue, un \xFAnico Cancel, y se revela ya (manda \`ready\`): no hay nada m\xE1s que esperar.
7077
+ // No es interacci\xF3n del usuario: el renderer no lo convierte en popup_clicked.
7078
+ var ddLoadFailed=false;
7079
+ function ddShowLoadError(name, reason){
7080
+ if(ddLoadFailed){ return; }
7081
+ ddLoadFailed=true;
7082
+ var why=(reason && reason.message) ? reason.message : String(reason);
7083
+ emitJSON(name, { message: why });
7084
+ progressEl.style.display='none';
7085
+ formWrapper.style.display='none';
7086
+ errorHint.style.display='none';
7087
+ loadErrorEl.textContent=DD_LABELS_ACTIVE.errorLoad;
7088
+ loadErrorEl.style.display='block';
7089
+ setLoading(false);
7090
+ updateButtons('load_error');
7091
+ ddReveal.reveal();
7092
+ }
7093
+
7017
7094
  var initialized=false;
7018
7095
  function initMF(){
7019
7096
  if(initialized || !window.magicfeedback) return initialized;
@@ -7037,6 +7114,7 @@ ${REVEAL_JS}
7037
7114
  // de native sin esta opci\xF3n la ignoran.
7038
7115
  autofocus:'navigation',
7039
7116
  onLoadedEvent:function(args){
7117
+ if(args && args.error){ ddShowLoadError('error:load', args.error); return; }
7040
7118
  // Idioma en el que native muestra el survey: manda sobre el del init para que el
7041
7119
  // chrome no se quede en ingl\xE9s delante de un survey traducido. Native < 2.3 no lo
7042
7120
  // env\xEDa: entonces el primero de formData.lang (el idioma por defecto).
@@ -7140,13 +7218,13 @@ ${REVEAL_JS}
7140
7218
  updateProgress({ progress:p&&p.progress, total:form.total, completed:false, followup:p&&p.followup });
7141
7219
  emitJSON('back', p);
7142
7220
  }
7143
- }).catch(function(e){ setLoading(false); emit('error:init'); });
7144
- }catch(e){ setLoading(false); emit('error:exception'); }
7221
+ }).catch(function(e){ ddShowLoadError('error:init', e); });
7222
+ }catch(e){ ddShowLoadError('error:exception', e); }
7145
7223
  return true;
7146
7224
  }
7147
7225
  var s=document.createElement('script'); s.src=${JSON.stringify(cdn)}; s.async=true;
7148
7226
  s.onload=function(){ initMF(); };
7149
- s.onerror=function(){ setLoading(false); emit('error:load'); };
7227
+ s.onerror=function(){ ddShowLoadError('error:load', 'survey script failed to load'); };
7150
7228
  document.head.appendChild(s);
7151
7229
  var tries=0, t=setInterval(function(){ if(initMF()||++tries>40){ clearInterval(t); } },150);
7152
7230
  })();
@@ -7265,6 +7343,7 @@ var ReactNativePopupRenderer = class {
7265
7343
  this.onCloseFn?.();
7266
7344
  break;
7267
7345
  default:
7346
+ if (typeof name === "string" && name.startsWith("error:")) this.emitReady(surveyId);
7268
7347
  break;
7269
7348
  }
7270
7349
  }
@@ -8393,8 +8472,6 @@ var DeepdotsPopups = class {
8393
8472
  this.renderer = createDefaultRenderer();
8394
8473
  /** Logger para el output de debug. Default `console`; el host puede inyectar el suyo en init(). */
8395
8474
  this.logger = console;
8396
- this.popupContainer = null;
8397
- // deprecated: mantenido para compatibilidad interna
8398
8475
  this.popupDefinitions = [];
8399
8476
  this.popupsLoaded = false;
8400
8477
  /**
@@ -8425,6 +8502,7 @@ var DeepdotsPopups = class {
8425
8502
  this.showProgressBar = void 0;
8426
8503
  /** CSS del host inyectado al final de los estilos del popup. */
8427
8504
  this.surveyCss = void 0;
8505
+ this.cspNonce = void 0;
8428
8506
  /** Identidad + sesión (Fase 1 tracking). Null hasta init(). */
8429
8507
  this.tracking = null;
8430
8508
  /** Capa de analytics (canal separado del feedback). Null hasta init(). */
@@ -8497,6 +8575,7 @@ var DeepdotsPopups = class {
8497
8575
  this.renderChrome = config.renderChrome ?? true;
8498
8576
  this.showProgressBar = config.showProgressBar;
8499
8577
  this.surveyCss = config.surveyCss;
8578
+ this.cspNonce = config.cspNonce;
8500
8579
  this.onFeedbackSession = config.onFeedbackSession;
8501
8580
  this.config = {
8502
8581
  apiKey: config.apiKey || void 0,
@@ -8597,7 +8676,6 @@ var DeepdotsPopups = class {
8597
8676
  this.initialized = true;
8598
8677
  this.log("SDK initialized", this.config);
8599
8678
  if (this.renderer.init) this.renderer.init();
8600
- this.setupPopupContainer();
8601
8679
  this.awaitingPopups = Boolean(this.config.apiKey && this.baseUrl);
8602
8680
  this.fetchPopupsFromServer().then((defs) => {
8603
8681
  if (!defs) this.dropPendingTriggers();
@@ -9568,27 +9646,6 @@ var DeepdotsPopups = class {
9568
9646
  listeners.delete(listener);
9569
9647
  }
9570
9648
  }
9571
- /** Setup popup container element */
9572
- setupPopupContainer() {
9573
- if (typeof document === "undefined") {
9574
- return;
9575
- }
9576
- this.popupContainer = document.createElement("div");
9577
- this.popupContainer.id = "deepdots-popup-container";
9578
- this.popupContainer.style.cssText = `
9579
- position: fixed;
9580
- top: 0;
9581
- left: 0;
9582
- width: 100%;
9583
- height: 100%;
9584
- display: none;
9585
- z-index: 999999;
9586
- background: rgba(0, 0, 0, 0.5);
9587
- justify-content: center;
9588
- align-items: center;
9589
- `;
9590
- document.body.appendChild(this.popupContainer);
9591
- }
9592
9649
  /** Render the popup UI */
9593
9650
  renderPopup(surveyId, productId, actions, style, title) {
9594
9651
  const userId = this.tracking?.getUserId() ?? this.config?.userId;
@@ -9596,54 +9653,25 @@ var DeepdotsPopups = class {
9596
9653
  const miniService = this.analytics?.getMiniService() ?? void 0;
9597
9654
  const analyticsFeedbackSessionId = this.analyticsFeedbackSessionId;
9598
9655
  this.log("tracking \xB7 survey identity \u2192", { userId: userId ?? null, sessionId: sessionId ?? null, miniService: miniService ?? null, analyticsFeedbackSessionId: analyticsFeedbackSessionId ?? null });
9599
- if (this.renderer) {
9600
- this.renderer.show(
9601
- surveyId,
9602
- productId,
9603
- actions,
9604
- (type, id, payload) => this.emitEvent(type, id, payload),
9605
- () => this.hidePopup(),
9606
- this.env,
9607
- userId,
9608
- style,
9609
- sessionId,
9610
- miniService,
9611
- analyticsFeedbackSessionId,
9612
- this.renderChrome,
9613
- { title, showProgressBar: this.showProgressBar, surveyCss: this.surveyCss, language: this.language }
9614
- );
9615
- return;
9616
- }
9617
- if (!this.popupContainer) return;
9618
- const container = this.popupContainer;
9619
- void Promise.resolve().then(() => (init_renderPopup(), renderPopup_exports)).then(({ renderPopup: renderPopup2 }) => {
9620
- renderPopup2(
9621
- container,
9622
- surveyId,
9623
- productId,
9624
- actions,
9625
- (type, id, payload) => this.emitEvent(type, id, payload),
9626
- () => this.hidePopup(),
9627
- this.env,
9628
- userId,
9629
- style,
9630
- sessionId,
9631
- miniService,
9632
- analyticsFeedbackSessionId,
9633
- { title, showProgressBar: this.showProgressBar, surveyCss: this.surveyCss, language: this.language }
9634
- );
9635
- });
9656
+ this.renderer.show(
9657
+ surveyId,
9658
+ productId,
9659
+ actions,
9660
+ (type, id, payload) => this.emitEvent(type, id, payload),
9661
+ () => this.hidePopup(),
9662
+ this.env,
9663
+ userId,
9664
+ style,
9665
+ sessionId,
9666
+ miniService,
9667
+ analyticsFeedbackSessionId,
9668
+ this.renderChrome,
9669
+ { title, showProgressBar: this.showProgressBar, surveyCss: this.surveyCss, language: this.language, cspNonce: this.cspNonce }
9670
+ );
9636
9671
  }
9637
9672
  /** Hide the popup */
9638
9673
  hidePopup() {
9639
- if (this.renderer) {
9640
- this.renderer.hide();
9641
- return;
9642
- }
9643
- if (this.popupContainer) {
9644
- this.popupContainer.style.display = "none";
9645
- this.popupContainer.innerHTML = "";
9646
- }
9674
+ this.renderer.hide();
9647
9675
  }
9648
9676
  /** Emit an event */
9649
9677
  emitEvent(type, surveyId, data) {