@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.
@@ -253,7 +253,8 @@ var init_labels = __esm({
253
253
  closeAria: "Close popup",
254
254
  loadingAria: "Loading survey",
255
255
  errorRequired: "Please answer the required question to continue.",
256
- errorSubmit: "An error occurred while submitting. Please try again or close the popup."
256
+ errorSubmit: "An error occurred while submitting. Please try again or close the popup.",
257
+ errorLoad: "We couldn't load this survey."
257
258
  };
258
259
  ES = {
259
260
  accept: "Enviar",
@@ -267,7 +268,8 @@ var init_labels = __esm({
267
268
  closeAria: "Cerrar ventana emergente",
268
269
  loadingAria: "Cargando encuesta",
269
270
  errorRequired: "Responde la pregunta obligatoria para continuar.",
270
- errorSubmit: "Se produjo un error al enviar. Int\xE9ntalo de nuevo o cierra la ventana."
271
+ errorSubmit: "Se produjo un error al enviar. Int\xE9ntalo de nuevo o cierra la ventana.",
272
+ errorLoad: "No se ha podido cargar la encuesta."
271
273
  };
272
274
  DA = {
273
275
  accept: "Send",
@@ -281,7 +283,8 @@ var init_labels = __esm({
281
283
  closeAria: "Luk pop op",
282
284
  loadingAria: "Indl\xE6ser unders\xF8gelse",
283
285
  errorRequired: "Besvar venligst det obligatoriske sp\xF8rgsm\xE5l for at forts\xE6tte.",
284
- errorSubmit: "Der opstod en fejl under afsendelsen. Pr\xF8v igen, eller luk pop op-vinduet."
286
+ errorSubmit: "Der opstod en fejl under afsendelsen. Pr\xF8v igen, eller luk pop op-vinduet.",
287
+ errorLoad: "Unders\xF8gelsen kunne ikke indl\xE6ses."
285
288
  };
286
289
  NO = {
287
290
  accept: "Send",
@@ -295,7 +298,8 @@ var init_labels = __esm({
295
298
  closeAria: "Lukk popup",
296
299
  loadingAria: "Laster unders\xF8kelse",
297
300
  errorRequired: "Svar p\xE5 det obligatoriske sp\xF8rsm\xE5let for \xE5 fortsette.",
298
- errorSubmit: "Det oppstod en feil under innsendingen. Pr\xF8v igjen, eller lukk popupen."
301
+ errorSubmit: "Det oppstod en feil under innsendingen. Pr\xF8v igjen, eller lukk popupen.",
302
+ errorLoad: "Unders\xF8kelsen kunne ikke lastes inn."
299
303
  };
300
304
  SV = {
301
305
  accept: "Skicka",
@@ -309,7 +313,8 @@ var init_labels = __esm({
309
313
  closeAria: "St\xE4ng popup",
310
314
  loadingAria: "Laddar unders\xF6kning",
311
315
  errorRequired: "Svara p\xE5 den obligatoriska fr\xE5gan f\xF6r att forts\xE4tta.",
312
- errorSubmit: "Ett fel uppstod vid inskickningen. F\xF6rs\xF6k igen eller st\xE4ng popupen."
316
+ errorSubmit: "Ett fel uppstod vid inskickningen. F\xF6rs\xF6k igen eller st\xE4ng popupen.",
317
+ errorLoad: "Enk\xE4ten kunde inte laddas."
313
318
  };
314
319
  FI = {
315
320
  accept: "L\xE4het\xE4",
@@ -323,7 +328,8 @@ var init_labels = __esm({
323
328
  closeAria: "Sulje ponnahdusikkuna",
324
329
  loadingAria: "Ladataan kysely\xE4",
325
330
  errorRequired: "Vastaa pakolliseen kysymykseen jatkaaksesi.",
326
- errorSubmit: "L\xE4hetyksess\xE4 tapahtui virhe. Yrit\xE4 uudelleen tai sulje ponnahdusikkuna."
331
+ errorSubmit: "L\xE4hetyksess\xE4 tapahtui virhe. Yrit\xE4 uudelleen tai sulje ponnahdusikkuna.",
332
+ errorLoad: "Kysely\xE4 ei voitu ladata."
327
333
  };
328
334
  DE = {
329
335
  accept: "Senden",
@@ -337,7 +343,8 @@ var init_labels = __esm({
337
343
  closeAria: "Popup schlie\xDFen",
338
344
  loadingAria: "Umfrage wird geladen",
339
345
  errorRequired: "Bitte beantworte die Pflichtfrage, um fortzufahren.",
340
- errorSubmit: "Beim Senden ist ein Fehler aufgetreten. Versuche es erneut oder schlie\xDFe das Popup."
346
+ errorSubmit: "Beim Senden ist ein Fehler aufgetreten. Versuche es erneut oder schlie\xDFe das Popup.",
347
+ errorLoad: "Die Umfrage konnte nicht geladen werden."
341
348
  };
342
349
  FR = {
343
350
  accept: "Envoyer",
@@ -351,7 +358,8 @@ var init_labels = __esm({
351
358
  closeAria: "Fermer la fen\xEAtre",
352
359
  loadingAria: "Chargement de l'enqu\xEAte",
353
360
  errorRequired: "Veuillez r\xE9pondre \xE0 la question obligatoire pour continuer.",
354
- errorSubmit: "Une erreur s'est produite lors de l'envoi. R\xE9essayez ou fermez la fen\xEAtre."
361
+ errorSubmit: "Une erreur s'est produite lors de l'envoi. R\xE9essayez ou fermez la fen\xEAtre.",
362
+ errorLoad: "Impossible de charger l'enqu\xEAte."
355
363
  };
356
364
  PT = {
357
365
  accept: "Enviar",
@@ -365,7 +373,8 @@ var init_labels = __esm({
365
373
  closeAria: "Fechar janela",
366
374
  loadingAria: "A carregar question\xE1rio",
367
375
  errorRequired: "Responda \xE0 pergunta obrigat\xF3ria para continuar.",
368
- errorSubmit: "Ocorreu um erro ao enviar. Tente novamente ou feche a janela."
376
+ errorSubmit: "Ocorreu um erro ao enviar. Tente novamente ou feche a janela.",
377
+ errorLoad: "N\xE3o foi poss\xEDvel carregar o inqu\xE9rito."
369
378
  };
370
379
  AR = {
371
380
  accept: "\u0625\u0631\u0633\u0627\u0644",
@@ -379,7 +388,8 @@ var init_labels = __esm({
379
388
  closeAria: "\u0625\u063A\u0644\u0627\u0642 \u0627\u0644\u0646\u0627\u0641\u0630\u0629 \u0627\u0644\u0645\u0646\u0628\u062B\u0642\u0629",
380
389
  loadingAria: "\u062C\u0627\u0631\u064D \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0627\u0633\u062A\u0628\u064A\u0627\u0646",
381
390
  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.",
382
- 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."
391
+ 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.",
392
+ errorLoad: "\u062A\u0639\u0630\u0651\u0631 \u062A\u062D\u0645\u064A\u0644 \u0627\u0644\u0627\u0633\u062A\u0628\u064A\u0627\u0646."
383
393
  };
384
394
  BN = {
385
395
  accept: "\u09AA\u09BE\u09A0\u09BE\u09A8",
@@ -393,7 +403,8 @@ var init_labels = __esm({
393
403
  closeAria: "\u09AA\u09AA\u0986\u09AA \u09AC\u09A8\u09CD\u09A7 \u0995\u09B0\u09C1\u09A8",
394
404
  loadingAria: "\u099C\u09B0\u09BF\u09AA \u09B2\u09CB\u09A1 \u09B9\u099A\u09CD\u099B\u09C7",
395
405
  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",
396
- 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"
406
+ 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",
407
+ errorLoad: "\u09B8\u09AE\u09C0\u0995\u09CD\u09B7\u09BE\u099F\u09BF \u09B2\u09CB\u09A1 \u0995\u09B0\u09BE \u09AF\u09BE\u09AF\u09BC\u09A8\u09BF\u0964"
397
408
  };
398
409
  ZH_CN = {
399
410
  accept: "\u53D1\u9001",
@@ -407,7 +418,8 @@ var init_labels = __esm({
407
418
  closeAria: "\u5173\u95ED\u5F39\u7A97",
408
419
  loadingAria: "\u6B63\u5728\u52A0\u8F7D\u95EE\u5377",
409
420
  errorRequired: "\u8BF7\u56DE\u7B54\u5FC5\u7B54\u9898\u540E\u7EE7\u7EED\u3002",
410
- errorSubmit: "\u63D0\u4EA4\u65F6\u51FA\u9519\u3002\u8BF7\u91CD\u8BD5\u6216\u5173\u95ED\u5F39\u7A97\u3002"
421
+ errorSubmit: "\u63D0\u4EA4\u65F6\u51FA\u9519\u3002\u8BF7\u91CD\u8BD5\u6216\u5173\u95ED\u5F39\u7A97\u3002",
422
+ errorLoad: "\u65E0\u6CD5\u52A0\u8F7D\u95EE\u5377\u3002"
411
423
  };
412
424
  LABELS = {
413
425
  en: EN,
@@ -2796,30 +2808,35 @@ __export(renderPopup_exports, {
2796
2808
  SURVEY_ALIGNMENT_STYLE_ID: () => SURVEY_ALIGNMENT_STYLE_ID,
2797
2809
  renderPopup: () => renderPopup
2798
2810
  });
2799
- function ensureMagicFeedbackStyles(_popup) {
2800
- const STYLE_ID = "magicfeedback-sdk-styles";
2801
- if (document.getElementById(STYLE_ID)) return;
2802
- const style = document.createElement("style");
2803
- style.id = STYLE_ID;
2804
- style.textContent = style_default;
2805
- document.head.appendChild(style);
2806
- }
2807
- function ensureSurveyAlignmentStyles() {
2808
- if (document.getElementById(SURVEY_ALIGNMENT_STYLE_ID)) return;
2809
- const style = document.createElement("style");
2810
- style.id = SURVEY_ALIGNMENT_STYLE_ID;
2811
- style.textContent = `
2811
+ function ensureStyle(id, css, nonce) {
2812
+ let el = document.getElementById(id);
2813
+ if (el && nonce && el.getAttribute("nonce") !== nonce) {
2814
+ el.remove();
2815
+ el = null;
2816
+ }
2817
+ if (!el) {
2818
+ el = document.createElement("style");
2819
+ el.id = id;
2820
+ if (nonce) el.setAttribute("nonce", nonce);
2821
+ el.textContent = css;
2822
+ document.head.appendChild(el);
2823
+ } else if (el.textContent !== css) {
2824
+ el.textContent = css;
2825
+ }
2826
+ return el;
2827
+ }
2828
+ function ensureMagicFeedbackStyles(nonce) {
2829
+ ensureStyle("magicfeedback-sdk-styles", style_default, nonce);
2830
+ }
2831
+ function ensureSurveyAlignmentStyles(nonce) {
2832
+ ensureStyle(SURVEY_ALIGNMENT_STYLE_ID, `
2812
2833
  .deepdots-popup .magicfeedback-container,
2813
2834
  .deepdots-popup .magicfeedback-form,
2814
2835
  .deepdots-popup .magicfeedback-div { padding-left: 0; padding-right: 0; }
2815
- `;
2816
- document.head.appendChild(style);
2817
- }
2818
- function ensureSpinnerStyles(_popup) {
2819
- if (document.getElementById("deepdots-spinner-styles")) return;
2820
- const style = document.createElement("style");
2821
- style.id = "deepdots-spinner-styles";
2822
- style.textContent = `
2836
+ `, nonce);
2837
+ }
2838
+ function ensureSpinnerStyles(nonce) {
2839
+ ensureStyle("deepdots-spinner-styles", `
2823
2840
  /* El enunciado trae margin-top:20px del CSS de surveys, que sumaba al hueco de la barra
2824
2841
  de progreso y descompensaba la simetr\xEDa. Solo se anula en el primero: entre preguntas
2825
2842
  de la misma p\xE1gina sigue separando. */
@@ -2827,14 +2844,10 @@ function ensureSpinnerStyles(_popup) {
2827
2844
  @keyframes ddspin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
2828
2845
  .mf-spinner { display:flex; justify-content:center; align-items:center; padding:8px 0; }
2829
2846
  .mf-spinner-circle { width:28px; height:28px; border:3px solid #e0e6ed; border-top-color:#1E293B; border-radius:50%; animation: ddspin 0.9s linear infinite; }
2830
- `;
2831
- document.head.appendChild(style);
2832
- }
2833
- function ensureResponsiveStyles(_popup) {
2834
- if (document.getElementById("deepdots-responsive-styles")) return;
2835
- const style = document.createElement("style");
2836
- style.id = "deepdots-responsive-styles";
2837
- style.textContent = `
2847
+ `, nonce);
2848
+ }
2849
+ function ensureResponsiveStyles(nonce) {
2850
+ ensureStyle("deepdots-responsive-styles", `
2838
2851
  /* Responsive adjustments */
2839
2852
  @media (max-width: 640px) {
2840
2853
  .deepdots-popup {
@@ -2867,32 +2880,20 @@ function ensureResponsiveStyles(_popup) {
2867
2880
  overflow-y: auto !important;
2868
2881
  }
2869
2882
  }
2870
- `;
2871
- document.head.appendChild(style);
2883
+ `, nonce);
2872
2884
  }
2873
- function ensureCustomCss(css) {
2885
+ function ensureCustomCss(css, nonce) {
2874
2886
  const STYLE_ID = "deepdots-custom-css";
2875
- const existing = document.getElementById(STYLE_ID);
2876
2887
  if (!css) {
2877
- existing?.remove();
2888
+ document.getElementById(STYLE_ID)?.remove();
2878
2889
  return;
2879
2890
  }
2880
- const el = existing ?? document.createElement("style");
2881
- el.id = STYLE_ID;
2882
- if (el.textContent !== css) el.textContent = css;
2883
- document.head.appendChild(el);
2891
+ document.head.appendChild(ensureStyle(STYLE_ID, css, nonce));
2884
2892
  }
2885
- function ensureFontFace(family, url) {
2893
+ function ensureFontFace(family, url, nonce) {
2886
2894
  const css = buildFontFaceCss(family, url);
2887
2895
  if (!css) return;
2888
- const STYLE_ID = "deepdots-font-face";
2889
- let el = document.getElementById(STYLE_ID);
2890
- if (!el) {
2891
- el = document.createElement("style");
2892
- el.id = STYLE_ID;
2893
- document.head.appendChild(el);
2894
- }
2895
- if (el.textContent !== css) el.textContent = css;
2896
+ ensureStyle("deepdots-font-face", css, nonce);
2896
2897
  }
2897
2898
  function hasFinePointer() {
2898
2899
  return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(pointer: fine)").matches;
@@ -2902,6 +2903,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2902
2903
  let stylesInjected = false;
2903
2904
  let pageDepth = 0;
2904
2905
  let onStartPage = false;
2906
+ const cspNonce = options?.cspNonce?.trim() || void 0;
2905
2907
  let labels = getLabels(options?.language);
2906
2908
  let actionLabels = resolveActionLabels(actions, options?.language);
2907
2909
  let direction = directionFor(options?.language);
@@ -2953,7 +2955,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2953
2955
  popup.style.fontFamily = familyValue;
2954
2956
  popup.style.setProperty("--deepdots-font", familyValue);
2955
2957
  popup.classList.add("deepdots-has-font");
2956
- if (font.url) ensureFontFace(font.family, font.url);
2958
+ if (font.url) ensureFontFace(font.family, font.url, cspNonce);
2957
2959
  }
2958
2960
  const header = document.createElement("div");
2959
2961
  header.className = "deepdots-popup-header";
@@ -3002,10 +3004,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3002
3004
  closeBtn.style.color = theme.textPrimary;
3003
3005
  closeBtn.style.transform = "scale(1)";
3004
3006
  };
3005
- closeBtn.onclick = () => {
3007
+ const closeFromChrome = () => {
3006
3008
  emit("popup_clicked", surveyId, { action: "close_icon" });
3007
3009
  onClose();
3008
3010
  };
3011
+ closeBtn.onclick = closeFromChrome;
3009
3012
  closeBtn.style.flex = "0 0 auto";
3010
3013
  closeBtn.style.marginLeft = "auto";
3011
3014
  header.appendChild(closeBtn);
@@ -3069,11 +3072,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3069
3072
  }
3070
3073
  progressFollowUp.style.display = p.followup ? "inline-block" : "none";
3071
3074
  }
3072
- ensureMagicFeedbackStyles(popup);
3073
- ensureSurveyAlignmentStyles();
3074
- ensureSpinnerStyles(popup);
3075
- ensureResponsiveStyles(popup);
3076
- ensureCustomCss(options?.surveyCss);
3075
+ ensureMagicFeedbackStyles(cspNonce);
3076
+ ensureSurveyAlignmentStyles(cspNonce);
3077
+ ensureSpinnerStyles(cspNonce);
3078
+ ensureResponsiveStyles(cspNonce);
3079
+ ensureCustomCss(options?.surveyCss, cspNonce);
3077
3080
  const containerContent = document.createElement("div");
3078
3081
  containerContent.id = "dd-content";
3079
3082
  containerContent.className = "deepdots-popup-container-content";
@@ -3113,6 +3116,12 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3113
3116
  formWrapper.appendChild(spinnerEl);
3114
3117
  formWrapper.appendChild(formHost);
3115
3118
  main.appendChild(formWrapper);
3119
+ const loadErrorEl = document.createElement("div");
3120
+ loadErrorEl.id = "dd-load-error";
3121
+ loadErrorEl.className = "deepdots-load-error";
3122
+ loadErrorEl.setAttribute("role", "alert");
3123
+ loadErrorEl.style.cssText = `display:none; width:100%; text-align:center; padding:24px 0; font-size:15px; color:${theme.textPrimary};`;
3124
+ main.appendChild(loadErrorEl);
3116
3125
  main.appendChild(errorHint);
3117
3126
  const footer = document.createElement("div");
3118
3127
  footer.id = "dd-footer";
@@ -3228,6 +3237,13 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3228
3237
  justify-content: center;
3229
3238
  text-align: center;
3230
3239
  `;
3240
+ const cancelButton = document.createElement("button");
3241
+ cancelButton.id = "dd-cancel";
3242
+ cancelButton.type = "button";
3243
+ cancelButton.className = "dd-nav-btn";
3244
+ cancelButton.textContent = actionLabels.decline;
3245
+ cancelButton.style.cssText = submitButton.style.cssText;
3246
+ cancelButton.onclick = closeFromChrome;
3231
3247
  submitButton.onclick = () => {
3232
3248
  if (!surveyCompletedEmitted) {
3233
3249
  emit("popup_clicked", surveyId, { action: "manual_send" });
@@ -3241,6 +3257,8 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3241
3257
  startButton.textContent = actionLabels.start;
3242
3258
  closeButton.textContent = actionLabels.complete;
3243
3259
  submitButton.textContent = actionLabels.accept;
3260
+ cancelButton.textContent = actionLabels.decline;
3261
+ loadErrorEl.textContent = labels.errorLoad;
3244
3262
  closeBtn.setAttribute("aria-label", labels.closeAria);
3245
3263
  spinnerEl.setAttribute("aria-label", labels.loadingAria);
3246
3264
  progressFollowUp.textContent = labels.followUp;
@@ -3251,10 +3269,12 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3251
3269
  startButton.style.display = "none";
3252
3270
  submitButton.style.display = "none";
3253
3271
  closeButton.style.display = "none";
3272
+ cancelButton.style.display = "none";
3254
3273
  footer.appendChild(submitButton);
3255
3274
  footer.appendChild(backButton);
3256
3275
  footer.appendChild(closeButton);
3257
3276
  footer.appendChild(startButton);
3277
+ footer.appendChild(cancelButton);
3258
3278
  containerContent.appendChild(main);
3259
3279
  footer.style.flex = "0 0 auto";
3260
3280
  containerContent.appendChild(footer);
@@ -3263,6 +3283,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3263
3283
  startButton.style.display = "none";
3264
3284
  submitButton.style.display = "none";
3265
3285
  closeButton.style.display = "none";
3286
+ cancelButton.style.display = "none";
3266
3287
  backButton.style.width = "";
3267
3288
  startButton.style.width = "";
3268
3289
  submitButton.style.width = "";
@@ -3292,6 +3313,9 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3292
3313
  case "error":
3293
3314
  setLoading(false);
3294
3315
  break;
3316
+ case "load_error":
3317
+ cancelButton.style.display = "inline-flex";
3318
+ break;
3295
3319
  }
3296
3320
  }
3297
3321
  function updateNavButtons() {
@@ -3313,6 +3337,20 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3313
3337
  img.style.cssText = "max-width:100%; height:auto; margin:0 auto 16px auto; display:block;";
3314
3338
  });
3315
3339
  }
3340
+ let loadFailed = false;
3341
+ function showLoadError(reason) {
3342
+ sdkWarn("[Deepdots] survey failed to load:", reason);
3343
+ if (loadFailed) return;
3344
+ loadFailed = true;
3345
+ progressEl.style.display = "none";
3346
+ formWrapper.style.display = "none";
3347
+ errorHint.style.display = "none";
3348
+ loadErrorEl.textContent = labels.errorLoad;
3349
+ loadErrorEl.style.display = "block";
3350
+ setLoading(false);
3351
+ updateButtons("load_error");
3352
+ reveal();
3353
+ }
3316
3354
  popup.appendChild(header);
3317
3355
  popup.appendChild(progressEl);
3318
3356
  popup.appendChild(containerContent);
@@ -3377,7 +3415,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3377
3415
  try {
3378
3416
  if (!import_native.default || typeof import_native.default.form !== "function") {
3379
3417
  sdkWarn("[MagicFeedback] form() no disponible. Fallback manual.");
3380
- setLoading(false);
3418
+ showLoadError("form() not available");
3381
3419
  return;
3382
3420
  }
3383
3421
  import_native.default.init(
@@ -3403,7 +3441,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3403
3441
  // quitado el `inert`. La apertura va aparte (ver autofocusOnOpen).
3404
3442
  autofocus: "navigation"
3405
3443
  };
3406
- generateOptions.onLoadedEvent = ({ formData, lang }) => {
3444
+ generateOptions.onLoadedEvent = ({ formData, lang, error }) => {
3445
+ if (error) {
3446
+ showLoadError(error);
3447
+ return;
3448
+ }
3407
3449
  const shownLang = typeof lang === "string" && lang.trim().length > 0 ? lang : void 0;
3408
3450
  const surveyLang = shownLang ?? (Array.isArray(formData?.lang) ? formData.lang.find((l) => typeof l === "string" && l.trim().length > 0) : void 0);
3409
3451
  if (surveyLang) applyLabels(surveyLang);
@@ -3502,11 +3544,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3502
3544
  };
3503
3545
  formInstance.generate(formDivId, generateOptions).catch((err) => {
3504
3546
  sdkError("[MagicFeedback] Error generating form:", err);
3505
- setLoading(false);
3547
+ showLoadError(err);
3506
3548
  });
3507
3549
  } catch (e) {
3508
3550
  sdkError("[MagicFeedback] Exception initializing form:", e);
3509
- setLoading(false);
3551
+ showLoadError(e);
3510
3552
  }
3511
3553
  }
3512
3554
  var import_native, SURVEY_ALIGNMENT_STYLE_ID;
@@ -3819,12 +3861,14 @@ function buildSurveyHtml(opts) {
3819
3861
  const startLabel = jsonForScript(initActionLabels.start);
3820
3862
  const completeLabel = jsonForScript(initActionLabels.complete);
3821
3863
  const submitLabel = jsonForScript(initActionLabels.accept);
3864
+ const cancelLabel = jsonForScript(initActionLabels.decline);
3822
3865
  const labelsTableJson = jsonForScript(LABELS);
3823
3866
  const actionOverridesJson = jsonForScript({
3824
3867
  accept: trimmedOrNull(opts.actions?.accept?.label),
3825
3868
  start: trimmedOrNull(opts.actions?.start?.label),
3826
3869
  complete: trimmedOrNull(opts.actions?.complete?.label),
3827
- back: trimmedOrNull(opts.actions?.back?.label)
3870
+ back: trimmedOrNull(opts.actions?.back?.label),
3871
+ decline: trimmedOrNull(opts.actions?.decline?.label)
3828
3872
  });
3829
3873
  const initLangJson = jsonForScript(opts.language ?? "");
3830
3874
  const initDirection = directionFor(opts.language);
@@ -3886,6 +3930,7 @@ body.dd-ready #dd-popup{visibility:visible}
3886
3930
  #dd-logo{max-height:40px;max-width:100%;object-fit:contain;display:block;margin:12px 0 0 0}
3887
3931
  .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}
3888
3932
  /* Pantalla final: el HTML del editor de la plataforma (imagen + texto), centrado. */
3933
+ .deepdots-load-error{display:none;width:100%;text-align:center;padding:24px 0;font-size:15px;color:${textPrimary}}
3889
3934
  .deepdots-success{display:none;width:100%;text-align:center;padding:24px 0;color:${textPrimary}}
3890
3935
  .deepdots-success img{max-width:100%;height:auto;margin:0 auto 16px auto;display:block}
3891
3936
  .deepdots-success p{margin:0;font-size:16px;font-weight:600;line-height:1.4}
@@ -3893,7 +3938,7 @@ body.dd-ready #dd-popup{visibility:visible}
3893
3938
  .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}
3894
3939
  /* Secundario como bot\xF3n de texto: sin borde ni fondo, para que el primario sea la \xFAnica CTA. */
3895
3940
  #dd-back{background:transparent;color:${textMuted};border:none}
3896
- #dd-start,#dd-complete,#dd-submit{background:#1E293B;color:#fff;border:none}
3941
+ #dd-start,#dd-complete,#dd-submit,#dd-cancel{background:#1E293B;color:#fff;border:none}
3897
3942
  #dd-submit:disabled,#dd-start:disabled,#dd-back:disabled,#dd-complete:disabled{opacity:0.6;cursor:not-allowed}
3898
3943
  .mf-spinner{display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}
3899
3944
  .mf-spinner-circle{width:28px;height:28px;border:3px solid #e0e6ed;border-top-color:#1E293B;border-radius:50%;animation:ddspin 0.9s linear infinite}
@@ -3934,6 +3979,7 @@ ${customCss}
3934
3979
  <div class="mf-spinner" id="dd-spinner" role="status" aria-label="${initLabels.loadingAria}"><div class="mf-spinner-circle"></div></div>
3935
3980
  <div id="mf"></div>
3936
3981
  </div>
3982
+ <div class="deepdots-load-error" id="dd-load-error" role="alert"></div>
3937
3983
  <div class="deepdots-error-hint" id="dd-error" role="alert" aria-live="polite"></div>
3938
3984
  </div>
3939
3985
  <div class="deepdots-popup-footer" id="dd-footer">
@@ -3941,6 +3987,7 @@ ${customCss}
3941
3987
  <button type="button" class="dd-nav-btn" id="dd-back"></button>
3942
3988
  <button type="button" class="dd-nav-btn" id="dd-complete"></button>
3943
3989
  <button type="button" class="dd-nav-btn" id="dd-start"></button>
3990
+ <button type="button" class="dd-nav-btn" id="dd-cancel"></button>
3944
3991
  </div>
3945
3992
  </div>
3946
3993
  </div>
@@ -3963,6 +4010,8 @@ ${customCss}
3963
4010
  var startBtn=document.getElementById('dd-start');
3964
4011
  var completeBtn=document.getElementById('dd-complete');
3965
4012
  var submitBtn=document.getElementById('dd-submit');
4013
+ var cancelBtn=document.getElementById('dd-cancel');
4014
+ var loadErrorEl=document.getElementById('dd-load-error');
3966
4015
  var titleEl=document.getElementById('dd-title');
3967
4016
  var progressEl=document.getElementById('dd-progress');
3968
4017
  var progressLabel=document.getElementById('dd-progress-label');
@@ -3992,6 +4041,8 @@ ${customCss}
3992
4041
  startBtn.textContent=DD_ACTION_OVERRIDES.start||next.start;
3993
4042
  completeBtn.textContent=DD_ACTION_OVERRIDES.complete||next.complete;
3994
4043
  submitBtn.textContent=DD_ACTION_OVERRIDES.accept||next.accept;
4044
+ cancelBtn.textContent=DD_ACTION_OVERRIDES.decline||next.decline;
4045
+ loadErrorEl.textContent=next.errorLoad;
3995
4046
  if(closeEl){ closeEl.setAttribute('aria-label',next.closeAria); }
3996
4047
  if(spinner){ spinner.setAttribute('aria-label',next.loadingAria); }
3997
4048
  progressFollowUp.textContent=next.followUp;
@@ -4046,6 +4097,7 @@ ${customCss}
4046
4097
  startBtn.textContent=${startLabel};
4047
4098
  completeBtn.textContent=${completeLabel};
4048
4099
  submitBtn.textContent=${submitLabel};
4100
+ cancelBtn.textContent=${cancelLabel};
4049
4101
 
4050
4102
  closeEl.onclick=function(){ emitJSON('popup_close'); };
4051
4103
  backBtn.onclick=function(){ if(window.DeepdotsForm && window.DeepdotsForm.back){ window.DeepdotsForm.back(); } };
@@ -4057,6 +4109,8 @@ ${customCss}
4057
4109
  }
4058
4110
  };
4059
4111
  completeBtn.onclick=function(){ emitJSON('popup_close'); };
4112
+ // \xDAnica acci\xF3n del survey que no carga: cierra igual que la X.
4113
+ cancelBtn.onclick=function(){ emitJSON('popup_close'); };
4060
4114
  var surveyCompletedEmitted=false;
4061
4115
  submitBtn.onclick=function(){
4062
4116
  if(surveyCompletedEmitted) return;
@@ -4098,6 +4152,7 @@ ${customCss}
4098
4152
  startBtn.style.display='none';
4099
4153
  submitBtn.style.display='none';
4100
4154
  completeBtn.style.display='none';
4155
+ cancelBtn.style.display='none';
4101
4156
  backBtn.style.width='';
4102
4157
  startBtn.style.width='';
4103
4158
  submitBtn.style.width='';
@@ -4113,6 +4168,8 @@ ${customCss}
4113
4168
  } else if(state==='completed'){
4114
4169
  completeBtn.style.display='inline-flex';
4115
4170
  completeBtn.style.width='100%';
4171
+ } else if(state==='load_error'){
4172
+ cancelBtn.style.display='inline-flex';
4116
4173
  }
4117
4174
  }
4118
4175
 
@@ -4159,6 +4216,26 @@ ${REVEAL_JS}
4159
4216
  }
4160
4217
  setLoading(true);
4161
4218
 
4219
+ // Survey que no ha cargado (406 de /info, sin red, bundle del CDN ca\xEDdo). Antes se emit\xEDa
4220
+ // \`loaded\` y se pintaba un Send sobre un survey vac\xEDo. Espejo de KMP: aviso traducido, la X
4221
+ // sigue, un \xFAnico Cancel, y se revela ya (manda \`ready\`): no hay nada m\xE1s que esperar.
4222
+ // No es interacci\xF3n del usuario: el renderer no lo convierte en popup_clicked.
4223
+ var ddLoadFailed=false;
4224
+ function ddShowLoadError(name, reason){
4225
+ if(ddLoadFailed){ return; }
4226
+ ddLoadFailed=true;
4227
+ var why=(reason && reason.message) ? reason.message : String(reason);
4228
+ emitJSON(name, { message: why });
4229
+ progressEl.style.display='none';
4230
+ formWrapper.style.display='none';
4231
+ errorHint.style.display='none';
4232
+ loadErrorEl.textContent=DD_LABELS_ACTIVE.errorLoad;
4233
+ loadErrorEl.style.display='block';
4234
+ setLoading(false);
4235
+ updateButtons('load_error');
4236
+ ddReveal.reveal();
4237
+ }
4238
+
4162
4239
  var initialized=false;
4163
4240
  function initMF(){
4164
4241
  if(initialized || !window.magicfeedback) return initialized;
@@ -4182,6 +4259,7 @@ ${REVEAL_JS}
4182
4259
  // de native sin esta opci\xF3n la ignoran.
4183
4260
  autofocus:'navigation',
4184
4261
  onLoadedEvent:function(args){
4262
+ if(args && args.error){ ddShowLoadError('error:load', args.error); return; }
4185
4263
  // Idioma en el que native muestra el survey: manda sobre el del init para que el
4186
4264
  // chrome no se quede en ingl\xE9s delante de un survey traducido. Native < 2.3 no lo
4187
4265
  // env\xEDa: entonces el primero de formData.lang (el idioma por defecto).
@@ -4285,13 +4363,13 @@ ${REVEAL_JS}
4285
4363
  updateProgress({ progress:p&&p.progress, total:form.total, completed:false, followup:p&&p.followup });
4286
4364
  emitJSON('back', p);
4287
4365
  }
4288
- }).catch(function(e){ setLoading(false); emit('error:init'); });
4289
- }catch(e){ setLoading(false); emit('error:exception'); }
4366
+ }).catch(function(e){ ddShowLoadError('error:init', e); });
4367
+ }catch(e){ ddShowLoadError('error:exception', e); }
4290
4368
  return true;
4291
4369
  }
4292
4370
  var s=document.createElement('script'); s.src=${JSON.stringify(cdn)}; s.async=true;
4293
4371
  s.onload=function(){ initMF(); };
4294
- s.onerror=function(){ setLoading(false); emit('error:load'); };
4372
+ s.onerror=function(){ ddShowLoadError('error:load', 'survey script failed to load'); };
4295
4373
  document.head.appendChild(s);
4296
4374
  var tries=0, t=setInterval(function(){ if(initMF()||++tries>40){ clearInterval(t); } },150);
4297
4375
  })();
@@ -4410,6 +4488,7 @@ var ReactNativePopupRenderer = class {
4410
4488
  this.onCloseFn?.();
4411
4489
  break;
4412
4490
  default:
4491
+ if (typeof name === "string" && name.startsWith("error:")) this.emitReady(surveyId);
4413
4492
  break;
4414
4493
  }
4415
4494
  }
@@ -5538,8 +5617,6 @@ var DeepdotsPopups = class {
5538
5617
  this.renderer = createDefaultRenderer();
5539
5618
  /** Logger para el output de debug. Default `console`; el host puede inyectar el suyo en init(). */
5540
5619
  this.logger = console;
5541
- this.popupContainer = null;
5542
- // deprecated: mantenido para compatibilidad interna
5543
5620
  this.popupDefinitions = [];
5544
5621
  this.popupsLoaded = false;
5545
5622
  /**
@@ -5570,6 +5647,7 @@ var DeepdotsPopups = class {
5570
5647
  this.showProgressBar = void 0;
5571
5648
  /** CSS del host inyectado al final de los estilos del popup. */
5572
5649
  this.surveyCss = void 0;
5650
+ this.cspNonce = void 0;
5573
5651
  /** Identidad + sesión (Fase 1 tracking). Null hasta init(). */
5574
5652
  this.tracking = null;
5575
5653
  /** Capa de analytics (canal separado del feedback). Null hasta init(). */
@@ -5642,6 +5720,7 @@ var DeepdotsPopups = class {
5642
5720
  this.renderChrome = config.renderChrome ?? true;
5643
5721
  this.showProgressBar = config.showProgressBar;
5644
5722
  this.surveyCss = config.surveyCss;
5723
+ this.cspNonce = config.cspNonce;
5645
5724
  this.onFeedbackSession = config.onFeedbackSession;
5646
5725
  this.config = {
5647
5726
  apiKey: config.apiKey || void 0,
@@ -5742,7 +5821,6 @@ var DeepdotsPopups = class {
5742
5821
  this.initialized = true;
5743
5822
  this.log("SDK initialized", this.config);
5744
5823
  if (this.renderer.init) this.renderer.init();
5745
- this.setupPopupContainer();
5746
5824
  this.awaitingPopups = Boolean(this.config.apiKey && this.baseUrl);
5747
5825
  this.fetchPopupsFromServer().then((defs) => {
5748
5826
  if (!defs) this.dropPendingTriggers();
@@ -6713,27 +6791,6 @@ var DeepdotsPopups = class {
6713
6791
  listeners.delete(listener);
6714
6792
  }
6715
6793
  }
6716
- /** Setup popup container element */
6717
- setupPopupContainer() {
6718
- if (typeof document === "undefined") {
6719
- return;
6720
- }
6721
- this.popupContainer = document.createElement("div");
6722
- this.popupContainer.id = "deepdots-popup-container";
6723
- this.popupContainer.style.cssText = `
6724
- position: fixed;
6725
- top: 0;
6726
- left: 0;
6727
- width: 100%;
6728
- height: 100%;
6729
- display: none;
6730
- z-index: 999999;
6731
- background: rgba(0, 0, 0, 0.5);
6732
- justify-content: center;
6733
- align-items: center;
6734
- `;
6735
- document.body.appendChild(this.popupContainer);
6736
- }
6737
6794
  /** Render the popup UI */
6738
6795
  renderPopup(surveyId, productId, actions, style, title) {
6739
6796
  const userId = this.tracking?.getUserId() ?? this.config?.userId;
@@ -6741,54 +6798,25 @@ var DeepdotsPopups = class {
6741
6798
  const miniService = this.analytics?.getMiniService() ?? void 0;
6742
6799
  const analyticsFeedbackSessionId = this.analyticsFeedbackSessionId;
6743
6800
  this.log("tracking \xB7 survey identity \u2192", { userId: userId ?? null, sessionId: sessionId ?? null, miniService: miniService ?? null, analyticsFeedbackSessionId: analyticsFeedbackSessionId ?? null });
6744
- if (this.renderer) {
6745
- this.renderer.show(
6746
- surveyId,
6747
- productId,
6748
- actions,
6749
- (type, id, payload) => this.emitEvent(type, id, payload),
6750
- () => this.hidePopup(),
6751
- this.env,
6752
- userId,
6753
- style,
6754
- sessionId,
6755
- miniService,
6756
- analyticsFeedbackSessionId,
6757
- this.renderChrome,
6758
- { title, showProgressBar: this.showProgressBar, surveyCss: this.surveyCss, language: this.language }
6759
- );
6760
- return;
6761
- }
6762
- if (!this.popupContainer) return;
6763
- const container = this.popupContainer;
6764
- void Promise.resolve().then(() => (init_renderPopup(), renderPopup_exports)).then(({ renderPopup: renderPopup2 }) => {
6765
- renderPopup2(
6766
- container,
6767
- surveyId,
6768
- productId,
6769
- actions,
6770
- (type, id, payload) => this.emitEvent(type, id, payload),
6771
- () => this.hidePopup(),
6772
- this.env,
6773
- userId,
6774
- style,
6775
- sessionId,
6776
- miniService,
6777
- analyticsFeedbackSessionId,
6778
- { title, showProgressBar: this.showProgressBar, surveyCss: this.surveyCss, language: this.language }
6779
- );
6780
- });
6801
+ this.renderer.show(
6802
+ surveyId,
6803
+ productId,
6804
+ actions,
6805
+ (type, id, payload) => this.emitEvent(type, id, payload),
6806
+ () => this.hidePopup(),
6807
+ this.env,
6808
+ userId,
6809
+ style,
6810
+ sessionId,
6811
+ miniService,
6812
+ analyticsFeedbackSessionId,
6813
+ this.renderChrome,
6814
+ { title, showProgressBar: this.showProgressBar, surveyCss: this.surveyCss, language: this.language, cspNonce: this.cspNonce }
6815
+ );
6781
6816
  }
6782
6817
  /** Hide the popup */
6783
6818
  hidePopup() {
6784
- if (this.renderer) {
6785
- this.renderer.hide();
6786
- return;
6787
- }
6788
- if (this.popupContainer) {
6789
- this.popupContainer.style.display = "none";
6790
- this.popupContainer.innerHTML = "";
6791
- }
6819
+ this.renderer.hide();
6792
6820
  }
6793
6821
  /** Emit an event */
6794
6822
  emitEvent(type, surveyId, data) {