@magicfeedback/popup-sdk 1.3.0 → 1.4.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/README.md CHANGED
@@ -128,6 +128,11 @@ Config fields:
128
128
  - `platform?: 'web' | 'android' | 'ios'` — defaults to `'web'`
129
129
  - `device?: DeviceInfo` — device context override, for hosts where the SDK cannot detect it
130
130
  - `contactAttributes?: Record<string, string | number | boolean>`
131
+ - `renderChrome?: boolean` — React Native only. Defaults to `true`. When `false`, the survey
132
+ WebView HTML is delivered transparent and edge-to-edge, without the SDK's own card, backdrop,
133
+ or positioning, so the host mounts its own `Modal`/container and controls the styling. This
134
+ avoids a "double modal" when the host already wraps the WebView. The survey stays fully
135
+ functional (message bridge, form, nav buttons, close). No effect on the web DOM popup.
131
136
  - `language?: string` — BCP-47 tag (`'en'`, `'en-US'`, …) used for popup language
132
137
  segmentation (`segments.lang`) and for the analytics context. If omitted it is
133
138
  auto-detected from `navigator.language`, falling back to the `Intl` locale (which is
@@ -196,6 +201,7 @@ session start/end and unhandled errors are automatic. The rest is instrumented b
196
201
  | `trackSearch(query, resultsCount, params?)` | Searches, with `has_results` derived |
197
202
  | `trackFunnelStep(funnel, step, taskId, params?)` | A funnel step, correlated by `taskId` |
198
203
  | `trackFindabilityFriction(topic, params?)` | Friction signal |
204
+ | `trackMeaningfulInteraction(interactionType, params?)` | A meaningful interaction, grouped by `interaction_type` |
199
205
  | `enterMiniService(name, entryPointType?)` / `exitMiniService(name)` | Mini-service, with duration |
200
206
  | `setUserAttributes(map)` / `setMetric(key, value)` | Breakdown dimensions / measurable values |
201
207
  | `setUserId(userId?)` | Login, logout or account switch |
package/dist/index.d.mts CHANGED
@@ -256,6 +256,15 @@ interface DeepdotsInitParams {
256
256
  * `setContactAttributes` after init (sends only when values changed from the last sync).
257
257
  */
258
258
  contactAttributes?: Record<string, string | number | boolean>;
259
+ /**
260
+ * Whether the SDK renders the popup "modal" (card + backdrop + positioning) around the survey.
261
+ * Default `true`. Only affects React Native (the survey WebView HTML); the web DOM popup is
262
+ * unaffected. With `false` the survey is delivered transparent and edge-to-edge so the host
263
+ * mounts its own Modal/container and controls the styling — avoids the "double modal" when the
264
+ * host already wraps the WebView. The survey stays fully functional (bridge, form, nav buttons,
265
+ * close). Ver `ReactNativePopupRenderer`.
266
+ */
267
+ renderChrome?: boolean;
259
268
  }
260
269
  interface DeepdotsConfig {
261
270
  /** API key for authentication */
@@ -388,7 +397,7 @@ interface PopupRenderer {
388
397
  /** Preparar recursos si aplica */
389
398
  init?(): void;
390
399
  /** Mostrar popup */
391
- show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string): void;
400
+ show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string, renderChrome?: boolean): void;
392
401
  /** Ocultar popup */
393
402
  hide(): void;
394
403
  }
@@ -402,7 +411,7 @@ declare class BrowserPopupRenderer implements PopupRenderer {
402
411
  private container;
403
412
  private visible;
404
413
  init(): void;
405
- show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string): void;
414
+ show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string, _renderChrome?: boolean): void;
406
415
  hide(): void;
407
416
  }
408
417
  /** Factoría para obtener renderer por defecto */
@@ -602,6 +611,8 @@ declare class DeepdotsPopups {
602
611
  private deferredExitTimers;
603
612
  private baseUrl;
604
613
  private env;
614
+ /** Si el SDK pinta el "modal" del popup (RN). Default true; false = el host lo envuelve. */
615
+ private renderChrome;
605
616
  /** Identidad + sesión (Fase 1 tracking). Null hasta init(). */
606
617
  private tracking;
607
618
  /** Capa de analytics (canal separado del feedback). Null hasta init(). */
@@ -673,6 +684,15 @@ declare class DeepdotsPopups {
673
684
  trackFindabilityFriction(frictionTopic: string, params?: Record<string, unknown>): void;
674
685
  /** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */
675
686
  trackFunnelStep(funnel: string, step: string, taskId: string, params?: Record<string, unknown>): void;
687
+ /**
688
+ * Product Effectiveness: una interacción significativa del usuario (`interaction_type` es la
689
+ * dimensión de agrupación, p. ej. `get_help`, `homepage`, `contact_support`).
690
+ *
691
+ * Existe como evento RESERVADO para que la convención no la invente cada host: un `track()`
692
+ * custom acaba como `deepdots_event_…` y no lo cuenta la analítica de effectiveness.
693
+ * Un `interactionType` vacío se descarta con warning (no aporta dato agrupable).
694
+ */
695
+ trackMeaningfulInteraction(interactionType: string, params?: Record<string, unknown>): void;
676
696
  /**
677
697
  * Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app).
678
698
  * No-op si tracking off. El evento se descarta (con warning) si el `channel` no es válido,
@@ -847,7 +867,7 @@ declare class ReactNativePopupRenderer implements PopupRenderer {
847
867
  private partialEmitted;
848
868
  constructor(options?: ReactNativeRendererOptions);
849
869
  init(): void;
850
- show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: EmitFn, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string): void;
870
+ show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: EmitFn, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string, renderChrome?: boolean): void;
851
871
  hide(): void;
852
872
  /**
853
873
  * El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a
@@ -880,6 +900,13 @@ interface BuildSurveyHtmlOptions {
880
900
  theme?: 'light' | 'dark';
881
901
  position?: 'bottom' | 'bottom-right' | 'bottom-left' | 'top' | 'top-right' | 'top-left' | 'center';
882
902
  actions?: PopupActions;
903
+ /**
904
+ * Renderiza el "modal" del popup (tarjeta con fondo/sombra/bordes + overlay + posicionamiento).
905
+ * Default `true`. Con `false` el HTML es transparente y llena el contenedor: el host monta su
906
+ * propio Modal/tarjeta y controla el estilo. El survey sigue siendo funcional (bridge, form,
907
+ * botones y botón de cerrar). Pensado para React Native cuando el host gestiona el contenedor.
908
+ */
909
+ chrome?: boolean;
883
910
  }
884
911
  declare function buildSurveyHtml(opts: BuildSurveyHtmlOptions): string;
885
912
 
package/dist/index.d.ts CHANGED
@@ -256,6 +256,15 @@ interface DeepdotsInitParams {
256
256
  * `setContactAttributes` after init (sends only when values changed from the last sync).
257
257
  */
258
258
  contactAttributes?: Record<string, string | number | boolean>;
259
+ /**
260
+ * Whether the SDK renders the popup "modal" (card + backdrop + positioning) around the survey.
261
+ * Default `true`. Only affects React Native (the survey WebView HTML); the web DOM popup is
262
+ * unaffected. With `false` the survey is delivered transparent and edge-to-edge so the host
263
+ * mounts its own Modal/container and controls the styling — avoids the "double modal" when the
264
+ * host already wraps the WebView. The survey stays fully functional (bridge, form, nav buttons,
265
+ * close). Ver `ReactNativePopupRenderer`.
266
+ */
267
+ renderChrome?: boolean;
259
268
  }
260
269
  interface DeepdotsConfig {
261
270
  /** API key for authentication */
@@ -388,7 +397,7 @@ interface PopupRenderer {
388
397
  /** Preparar recursos si aplica */
389
398
  init?(): void;
390
399
  /** Mostrar popup */
391
- show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string): void;
400
+ show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string, renderChrome?: boolean): void;
392
401
  /** Ocultar popup */
393
402
  hide(): void;
394
403
  }
@@ -402,7 +411,7 @@ declare class BrowserPopupRenderer implements PopupRenderer {
402
411
  private container;
403
412
  private visible;
404
413
  init(): void;
405
- show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string): void;
414
+ show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string, _renderChrome?: boolean): void;
406
415
  hide(): void;
407
416
  }
408
417
  /** Factoría para obtener renderer por defecto */
@@ -602,6 +611,8 @@ declare class DeepdotsPopups {
602
611
  private deferredExitTimers;
603
612
  private baseUrl;
604
613
  private env;
614
+ /** Si el SDK pinta el "modal" del popup (RN). Default true; false = el host lo envuelve. */
615
+ private renderChrome;
605
616
  /** Identidad + sesión (Fase 1 tracking). Null hasta init(). */
606
617
  private tracking;
607
618
  /** Capa de analytics (canal separado del feedback). Null hasta init(). */
@@ -673,6 +684,15 @@ declare class DeepdotsPopups {
673
684
  trackFindabilityFriction(frictionTopic: string, params?: Record<string, unknown>): void;
674
685
  /** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */
675
686
  trackFunnelStep(funnel: string, step: string, taskId: string, params?: Record<string, unknown>): void;
687
+ /**
688
+ * Product Effectiveness: una interacción significativa del usuario (`interaction_type` es la
689
+ * dimensión de agrupación, p. ej. `get_help`, `homepage`, `contact_support`).
690
+ *
691
+ * Existe como evento RESERVADO para que la convención no la invente cada host: un `track()`
692
+ * custom acaba como `deepdots_event_…` y no lo cuenta la analítica de effectiveness.
693
+ * Un `interactionType` vacío se descarta con warning (no aporta dato agrupable).
694
+ */
695
+ trackMeaningfulInteraction(interactionType: string, params?: Record<string, unknown>): void;
676
696
  /**
677
697
  * Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app).
678
698
  * No-op si tracking off. El evento se descarta (con warning) si el `channel` no es válido,
@@ -847,7 +867,7 @@ declare class ReactNativePopupRenderer implements PopupRenderer {
847
867
  private partialEmitted;
848
868
  constructor(options?: ReactNativeRendererOptions);
849
869
  init(): void;
850
- show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: EmitFn, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string): void;
870
+ show(surveyId: string, productId: string, actions: PopupActions | undefined, emit: EmitFn, onClose: () => void, env?: string, userId?: string, style?: PopupStyle, sessionId?: string, miniService?: string, analyticsFeedbackSessionId?: string, renderChrome?: boolean): void;
851
871
  hide(): void;
852
872
  /**
853
873
  * El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a
@@ -880,6 +900,13 @@ interface BuildSurveyHtmlOptions {
880
900
  theme?: 'light' | 'dark';
881
901
  position?: 'bottom' | 'bottom-right' | 'bottom-left' | 'top' | 'top-right' | 'top-left' | 'center';
882
902
  actions?: PopupActions;
903
+ /**
904
+ * Renderiza el "modal" del popup (tarjeta con fondo/sombra/bordes + overlay + posicionamiento).
905
+ * Default `true`. Con `false` el HTML es transparente y llena el contenedor: el host monta su
906
+ * propio Modal/tarjeta y controla el estilo. El survey sigue siendo funcional (bridge, form,
907
+ * botones y botón de cerrar). Pensado para React Native cuando el host gestiona el contenedor.
908
+ */
909
+ chrome?: boolean;
883
910
  }
884
911
  declare function buildSurveyHtml(opts: BuildSurveyHtmlOptions): string;
885
912
 
package/dist/index.js CHANGED
@@ -2542,7 +2542,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2542
2542
  };
2543
2543
  generateOptions.onLoadedEvent = ({ formData }) => {
2544
2544
  try {
2545
- } catch (e) {
2545
+ } catch {
2546
2546
  }
2547
2547
  const s = formData?.style;
2548
2548
  if (s && !stylesInjected) {
@@ -2720,6 +2720,10 @@ var POPUP_TRIGGER_CONDITION_STATUSES = [
2720
2720
  ];
2721
2721
 
2722
2722
  // src/triggers/index.ts
2723
+ function toNumericValue(value, fallback) {
2724
+ const n = Number(value);
2725
+ return Number.isFinite(n) ? n : fallback;
2726
+ }
2723
2727
  function setupTrigger(popups, trigger) {
2724
2728
  switch (trigger.type) {
2725
2729
  case "time":
@@ -2740,14 +2744,14 @@ function setupTrigger(popups, trigger) {
2740
2744
  }
2741
2745
  }
2742
2746
  function setupTimeTrigger(popups, trigger) {
2743
- const delay = trigger.value || 5e3;
2747
+ const delay = toNumericValue(trigger.value, 5e3);
2744
2748
  setTimeout(() => {
2745
2749
  popups.triggerSurvey(trigger.surveyId, trigger.popupId);
2746
2750
  }, delay);
2747
2751
  popups.debug(`Time trigger set for ${delay}ms`);
2748
2752
  }
2749
2753
  function setupScrollTrigger(popups, trigger) {
2750
- const threshold = trigger.value || 50;
2754
+ const threshold = toNumericValue(trigger.value, 50);
2751
2755
  const handler = () => {
2752
2756
  const scrollPercentage = window.scrollY / (document.documentElement.scrollHeight - window.innerHeight) * 100;
2753
2757
  if (scrollPercentage >= threshold) {
@@ -2927,6 +2931,16 @@ function buildSurveyHtml(opts) {
2927
2931
  const colorScheme = isDark ? "dark" : "light";
2928
2932
  const textPrimary = isDark ? "#f0f0f0" : "#111";
2929
2933
  const pos = POSITION_MAP[opts.position ?? "center"] ?? POSITION_MAP.center;
2934
+ const chrome = opts.chrome !== false;
2935
+ const bodyBg = chrome ? pos.background : "transparent";
2936
+ const bodyPadding = chrome ? pos.padding : "0";
2937
+ const bodyDisplay = chrome ? "flex" : "block";
2938
+ const cardBg = chrome ? popupBg : "transparent";
2939
+ const cardShadow = chrome ? "0 4px 6px rgba(0,0,0,0.1)" : "none";
2940
+ const cardRadius = chrome ? "8px" : "0";
2941
+ const cardMaxWidth = chrome ? "600px" : "none";
2942
+ const cardWidth = chrome ? "90%" : "100%";
2943
+ const cardMinHeight = chrome ? "200px" : "0";
2930
2944
  const backLabel = JSON.stringify(opts.actions?.back?.label ?? "Back");
2931
2945
  const startLabel = JSON.stringify(opts.actions?.start?.label ?? "Start survey");
2932
2946
  const completeLabel = JSON.stringify(opts.actions?.complete?.label ?? "Complete survey");
@@ -2938,8 +2952,8 @@ function buildSurveyHtml(opts) {
2938
2952
  ${style_default}
2939
2953
  ${fontFaceCss}
2940
2954
  html,body{margin:0;padding:0;height:100%;font-family:${fontFamilyValue}}
2941
- body{display:flex;justify-content:${pos.justifyContent};align-items:${pos.alignItems};background:${pos.background};padding:${pos.padding};box-sizing:border-box}
2942
- .deepdots-popup{position:relative;display:flex;flex-direction:column;justify-content:flex-start;background:${popupBg};color-scheme:${colorScheme};border-radius:8px;padding:24px;box-shadow:0 4px 6px rgba(0,0,0,0.1);max-width:600px;width:90%;min-height:200px;box-sizing:border-box}
2955
+ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${pos.alignItems};background:${bodyBg};padding:${bodyPadding};box-sizing:border-box}
2956
+ .deepdots-popup{position:relative;display:flex;flex-direction:column;justify-content:flex-start;background:${cardBg};color-scheme:${colorScheme};border-radius:${cardRadius};padding:24px;box-shadow:${cardShadow};max-width:${cardMaxWidth};width:${cardWidth};min-height:${cardMinHeight};box-sizing:border-box}
2943
2957
  .deepdots-popup-header{display:flex;justify-content:flex-end;align-items:center;width:100%}
2944
2958
  #dd-close{background:transparent;border:none;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:8px;cursor:pointer;color:${textPrimary};padding:4px}
2945
2959
  .deepdots-popup-container-content{display:flex;flex-direction:column;padding:0 20px 12px 20px;max-height:80vh;overflow:hidden;box-sizing:border-box}
@@ -2997,6 +3011,7 @@ body{display:flex;justify-content:${pos.justifyContent};align-items:${pos.alignI
2997
3011
  function emit(s){ try{ if(window.ReactNativeWebView){ window.ReactNativeWebView.postMessage(s); } }catch(e){} }
2998
3012
  function emitJSON(n,p){ try{ emit(JSON.stringify({name:n,payload:p||{}})); }catch(e){} }
2999
3013
  window.DeepdotsClose = function(){ emitJSON('popup_close'); };
3014
+ var chromeOn=${chrome ? "true" : "false"};
3000
3015
 
3001
3016
  var popup=document.getElementById('dd-popup');
3002
3017
  var main=document.getElementById('dd-main');
@@ -3072,7 +3087,7 @@ body{display:flex;justify-content:${pos.justifyContent};align-items:${pos.alignI
3072
3087
  var s=args && args.formData ? args.formData.style : null;
3073
3088
  if(s && !stylesInjected){
3074
3089
  stylesInjected=true;
3075
- if(s.boxBackgroundColor){ popup.style.background=s.boxBackgroundColor; }
3090
+ if(chromeOn && s.boxBackgroundColor){ popup.style.background=s.boxBackgroundColor; }
3076
3091
  if(s.contentAlign){ main.style.justifyContent = s.contentAlign==='center' ? 'center' : 'flex-start'; }
3077
3092
  if(s.buttonPrimaryColor){
3078
3093
  submitBtn.style.background=s.buttonPrimaryColor; submitBtn.style.border='none'; submitBtn.style.color='#fff';
@@ -3162,7 +3177,7 @@ var ReactNativePopupRenderer = class {
3162
3177
  }
3163
3178
  init() {
3164
3179
  }
3165
- show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
3180
+ show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, renderChrome) {
3166
3181
  this.emitFn = emit;
3167
3182
  this.onCloseFn = onClose;
3168
3183
  this.currentSurveyId = surveyId;
@@ -3177,7 +3192,9 @@ var ReactNativePopupRenderer = class {
3177
3192
  font: style?.font,
3178
3193
  theme: style?.theme,
3179
3194
  position: style?.position,
3180
- actions
3195
+ actions,
3196
+ // renderChrome:false (init) → HTML sin tarjeta/overlay: el host lo envuelve en su Modal.
3197
+ chrome: renderChrome
3181
3198
  });
3182
3199
  if (this.options.onShow) {
3183
3200
  this.options.onShow({ surveyId, productId, html });
@@ -3268,7 +3285,7 @@ var BrowserPopupRenderer = class {
3268
3285
  document.body.appendChild(this.container);
3269
3286
  }
3270
3287
  }
3271
- show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
3288
+ show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, _renderChrome) {
3272
3289
  if (this.visible) return;
3273
3290
  if (!this.container || !document.body.contains(this.container)) this.init();
3274
3291
  if (!this.container) return;
@@ -4129,6 +4146,8 @@ var DeepdotsPopups = class {
4129
4146
  this.deferredExitTimers = [];
4130
4147
  this.baseUrl = "";
4131
4148
  this.env = "production";
4149
+ /** Si el SDK pinta el "modal" del popup (RN). Default true; false = el host lo envuelve. */
4150
+ this.renderChrome = true;
4132
4151
  /** Identidad + sesión (Fase 1 tracking). Null hasta init(). */
4133
4152
  this.tracking = null;
4134
4153
  /** Capa de analytics (canal separado del feedback). Null hasta init(). */
@@ -4179,6 +4198,7 @@ var DeepdotsPopups = class {
4179
4198
  const env = resolveEnvironment(config.nodeEnv);
4180
4199
  this.baseUrl = env.apiBaseUrl;
4181
4200
  this.env = config.nodeEnv || "production";
4201
+ this.renderChrome = config.renderChrome ?? true;
4182
4202
  this.config = {
4183
4203
  apiKey: config.apiKey || void 0,
4184
4204
  debug: config.debug || false,
@@ -4364,6 +4384,22 @@ var DeepdotsPopups = class {
4364
4384
  trackFunnelStep(funnel, step, taskId, params) {
4365
4385
  this.track("deepdots_funnel_step", { funnel, step, task_id: taskId, ...params ?? {} });
4366
4386
  }
4387
+ /**
4388
+ * Product Effectiveness: una interacción significativa del usuario (`interaction_type` es la
4389
+ * dimensión de agrupación, p. ej. `get_help`, `homepage`, `contact_support`).
4390
+ *
4391
+ * Existe como evento RESERVADO para que la convención no la invente cada host: un `track()`
4392
+ * custom acaba como `deepdots_event_…` y no lo cuenta la analítica de effectiveness.
4393
+ * Un `interactionType` vacío se descarta con warning (no aporta dato agrupable).
4394
+ */
4395
+ trackMeaningfulInteraction(interactionType, params) {
4396
+ const type = typeof interactionType === "string" ? interactionType.trim() : "";
4397
+ if (!type) {
4398
+ console.warn("[DeepdotsPopups] trackMeaningfulInteraction discarded: empty interaction_type");
4399
+ return;
4400
+ }
4401
+ this.track("deepdots_meaningful_interaction", { interaction_type: type, ...params ?? {} });
4402
+ }
4367
4403
  /**
4368
4404
  * Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app).
4369
4405
  * No-op si tracking off. El evento se descarta (con warning) si el `channel` no es válido,
@@ -4964,7 +5000,8 @@ var DeepdotsPopups = class {
4964
5000
  style,
4965
5001
  sessionId,
4966
5002
  miniService,
4967
- analyticsFeedbackSessionId
5003
+ analyticsFeedbackSessionId,
5004
+ this.renderChrome
4968
5005
  );
4969
5006
  return;
4970
5007
  }