@magicfeedback/popup-sdk 1.1.4 → 1.1.6

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
@@ -144,6 +144,64 @@ var init_tracking_manager = __esm({
144
144
  }
145
145
  });
146
146
 
147
+ // src/ui/font.ts
148
+ function fontFormatFromUrl(url) {
149
+ const clean = url.split("?")[0].split("#")[0];
150
+ const ext = clean.slice(clean.lastIndexOf(".") + 1).toLowerCase();
151
+ return FORMAT_BY_EXT[ext];
152
+ }
153
+ function sanitizeFamily(family) {
154
+ return family.replace(/[^A-Za-z0-9 ._-]/g, "").trim();
155
+ }
156
+ function isSafeFontUrl(url) {
157
+ if (!/^(https?:|data:)/i.test(url)) return false;
158
+ return !/[\x00-\x20"<>\\]/.test(url);
159
+ }
160
+ function buildFontFamilyValue(family) {
161
+ const safe = sanitizeFamily(family);
162
+ return `"${safe}", -apple-system, system-ui, sans-serif`;
163
+ }
164
+ function buildFontFaceCss(family, url) {
165
+ if (!url || !isSafeFontUrl(url)) return "";
166
+ const safe = sanitizeFamily(family);
167
+ const fmt = fontFormatFromUrl(url);
168
+ const src = fmt ? `url("${url}") format("${fmt}")` : `url("${url}")`;
169
+ return `@font-face{font-family:"${safe}";src:${src};font-display:swap;}`;
170
+ }
171
+ var FORMAT_BY_EXT;
172
+ var init_font = __esm({
173
+ "src/ui/font.ts"() {
174
+ "use strict";
175
+ FORMAT_BY_EXT = {
176
+ woff2: "woff2",
177
+ woff: "woff",
178
+ ttf: "truetype",
179
+ otf: "opentype"
180
+ };
181
+ }
182
+ });
183
+
184
+ // src/util/logger.ts
185
+ function setLogger(logger) {
186
+ active = logger ?? console;
187
+ }
188
+ function sdkLog(...args) {
189
+ active.log(...args);
190
+ }
191
+ function sdkWarn(...args) {
192
+ (active.warn ?? active.log)(...args);
193
+ }
194
+ function sdkError(...args) {
195
+ (active.error ?? active.log)(...args);
196
+ }
197
+ var active;
198
+ var init_logger = __esm({
199
+ "src/util/logger.ts"() {
200
+ "use strict";
201
+ active = console;
202
+ }
203
+ });
204
+
147
205
  // node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js
148
206
  var require_magicfeedback_sdk_browser = __commonJS({
149
207
  "node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js"(exports, module2) {
@@ -2213,13 +2271,22 @@ var init_style = __esm({
2213
2271
  padding: 0;
2214
2272
  font-weight: 900;
2215
2273
  color: #222;
2216
- font-family: 'Montserrat';
2274
+ font-family: var(--deepdots-font, 'Montserrat');
2217
2275
  font-size: 18px;
2218
2276
  text-transform: uppercase;
2219
2277
  text-align: center;
2220
2278
  margin-bottom: 40px;
2221
2279
  }
2222
2280
 
2281
+ /* Los controles de formulario no heredan font-family por defecto: cuando hay
2282
+ fuente custom (clase deepdots-has-font), los forzamos a usar la del contenedor. */
2283
+ .deepdots-popup.deepdots-has-font button,
2284
+ .deepdots-popup.deepdots-has-font input,
2285
+ .deepdots-popup.deepdots-has-font textarea,
2286
+ .deepdots-popup.deepdots-has-font select {
2287
+ font-family: var(--deepdots-font, inherit);
2288
+ }
2289
+
2223
2290
  .deepdots-popup h6 {
2224
2291
  margin-bottom: 1rem;
2225
2292
  }
@@ -2992,6 +3059,18 @@ function ensureResponsiveStyles(_popup) {
2992
3059
  `;
2993
3060
  document.head.appendChild(style);
2994
3061
  }
3062
+ function ensureFontFace(family, url) {
3063
+ const css = buildFontFaceCss(family, url);
3064
+ if (!css) return;
3065
+ const STYLE_ID = "deepdots-font-face";
3066
+ let el = document.getElementById(STYLE_ID);
3067
+ if (!el) {
3068
+ el = document.createElement("style");
3069
+ el.id = STYLE_ID;
3070
+ document.head.appendChild(el);
3071
+ }
3072
+ if (el.textContent !== css) el.textContent = css;
3073
+ }
2995
3074
  async function renderPopup(container, surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
2996
3075
  let surveyCompletedEmitted = false;
2997
3076
  let stylesInjected = false;
@@ -3027,6 +3106,14 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3027
3106
  width: 90%;
3028
3107
  min-height: 200px;
3029
3108
  `;
3109
+ const font = style?.font;
3110
+ if (font?.family) {
3111
+ const familyValue = buildFontFamilyValue(font.family);
3112
+ popup.style.fontFamily = familyValue;
3113
+ popup.style.setProperty("--deepdots-font", familyValue);
3114
+ popup.classList.add("deepdots-has-font");
3115
+ if (font.url) ensureFontFace(font.family, font.url);
3116
+ }
3030
3117
  const header = document.createElement("div");
3031
3118
  header.className = "deepdots-popup-header";
3032
3119
  header.style.cssText = "display:flex; justify-content:flex-end; align-items:center; width:100%;";
@@ -3293,7 +3380,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3293
3380
  let formInstance = null;
3294
3381
  try {
3295
3382
  if (!import_native.default || typeof import_native.default.form !== "function") {
3296
- console.warn("[MagicFeedback] form() no disponible. Fallback manual.");
3383
+ sdkWarn("[MagicFeedback] form() no disponible. Fallback manual.");
3297
3384
  setLoading(false);
3298
3385
  return;
3299
3386
  }
@@ -3380,7 +3467,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3380
3467
  }
3381
3468
  }
3382
3469
  if (s.startMessage && s.startMessage !== "") {
3383
- console.log(s.startMessage);
3470
+ sdkLog(s.startMessage);
3384
3471
  updateButtons("start");
3385
3472
  } else {
3386
3473
  updateButtons("in_progress_first");
@@ -3435,11 +3522,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3435
3522
  }
3436
3523
  };
3437
3524
  formInstance.generate(formDivId, generateOptions).catch((err) => {
3438
- console.error("[MagicFeedback] Error generating form:", err);
3525
+ sdkError("[MagicFeedback] Error generating form:", err);
3439
3526
  setLoading(false);
3440
3527
  });
3441
3528
  } catch (e) {
3442
- console.error("[MagicFeedback] Exception initializing form:", e);
3529
+ sdkError("[MagicFeedback] Exception initializing form:", e);
3443
3530
  setLoading(false);
3444
3531
  }
3445
3532
  }
@@ -3448,6 +3535,8 @@ var init_renderPopup = __esm({
3448
3535
  "src/ui/renderPopup.ts"() {
3449
3536
  "use strict";
3450
3537
  init_tracking_manager();
3538
+ init_font();
3539
+ init_logger();
3451
3540
  import_native = __toESM(require_magicfeedback_sdk_browser());
3452
3541
  init_style();
3453
3542
  }
@@ -3664,6 +3753,7 @@ function resolveEnvironment(nodeEnv) {
3664
3753
  init_tracking_manager();
3665
3754
 
3666
3755
  // src/ui/surveyHtml.ts
3756
+ init_font();
3667
3757
  function buildSurveyHtml(opts) {
3668
3758
  const env = opts.env === "development" ? "dev" : "prod";
3669
3759
  const version = opts.version ?? "2.2.4";
@@ -3672,10 +3762,12 @@ function buildSurveyHtml(opts) {
3672
3762
  const pid = JSON.stringify(opts.productId);
3673
3763
  const profileJson = JSON.stringify(opts.profile ?? []);
3674
3764
  const metaJson = JSON.stringify(opts.metadata ?? []);
3765
+ const fontFaceCss = opts.font ? buildFontFaceCss(opts.font.family, opts.font.url) : "";
3766
+ const fontFamilyValue = opts.font ? buildFontFamilyValue(opts.font.family) : "-apple-system,system-ui,sans-serif";
3675
3767
  return `<!DOCTYPE html><html><head>
3676
3768
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
3677
3769
  <meta name="color-scheme" content="light"/>
3678
- <style>html,body{margin:0;padding:0;background:transparent;font-family:-apple-system,system-ui,sans-serif}#mf{width:100%;box-sizing:border-box}#mf *{max-width:100%;box-sizing:border-box}</style>
3770
+ <style>${fontFaceCss}html,body{margin:0;padding:0;background:transparent;font-family:${fontFamilyValue}}#mf{width:100%;box-sizing:border-box}#mf *{max-width:100%;box-sizing:border-box}</style>
3679
3771
  </head><body>
3680
3772
  <div id="mf"></div>
3681
3773
  <script>
@@ -3716,6 +3808,7 @@ function buildSurveyHtml(opts) {
3716
3808
  }
3717
3809
 
3718
3810
  // src/platform/react-native-renderer.ts
3811
+ init_logger();
3719
3812
  var ReactNativePopupRenderer = class {
3720
3813
  constructor(options = {}) {
3721
3814
  this.options = options;
@@ -3726,17 +3819,17 @@ var ReactNativePopupRenderer = class {
3726
3819
  }
3727
3820
  init() {
3728
3821
  }
3729
- show(surveyId, productId, _actions, emit, onClose, env = "production", userId, _style, sessionId, miniService, analyticsFeedbackSessionId) {
3822
+ show(surveyId, productId, _actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
3730
3823
  this.emitFn = emit;
3731
3824
  this.onCloseFn = onClose;
3732
3825
  this.currentSurveyId = surveyId;
3733
3826
  this.partialEmitted = false;
3734
3827
  const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null, analyticsFeedbackSessionId ?? null);
3735
- const html = buildSurveyHtml({ surveyId, productId, env, profile, metadata });
3828
+ const html = buildSurveyHtml({ surveyId, productId, env, profile, metadata, font: style?.font });
3736
3829
  if (this.options.onShow) {
3737
3830
  this.options.onShow({ surveyId, productId, html });
3738
3831
  } else {
3739
- console.warn(
3832
+ sdkWarn(
3740
3833
  "[Deepdots] ReactNativePopupRenderer sin onShow: pasa { onShow } a new ReactNativePopupRenderer({...}) para montar el WebView del survey."
3741
3834
  );
3742
3835
  }
@@ -3853,12 +3946,13 @@ function createDefaultRenderer() {
3853
3946
  init_tracking_manager();
3854
3947
 
3855
3948
  // src/analytics/analytics-manager.ts
3856
- var dryRunSink = (payload) => {
3857
- console.log(
3949
+ function createDryRunSink(log = console.log) {
3950
+ return (payload) => log(
3858
3951
  "[DeepdotsAnalytics] (dry-run \xB7 NO enviado \xB7 sin init.analytics) POST /sdk/feedback \u2192",
3859
3952
  JSON.stringify(payload, null, 2)
3860
3953
  );
3861
- };
3954
+ }
3955
+ var dryRunSink = createDryRunSink();
3862
3956
  var AnalyticsManager = class {
3863
3957
  constructor(options = {}) {
3864
3958
  this.events = [];
@@ -4374,7 +4468,7 @@ var CrashReporter = class {
4374
4468
  }
4375
4469
  /** Instala los handlers globales de errores no capturados (no-op sin `window`). */
4376
4470
  install() {
4377
- if (typeof window === "undefined") return;
4471
+ if (typeof window === "undefined" || typeof window.addEventListener !== "function") return;
4378
4472
  window.addEventListener("error", (e) => {
4379
4473
  if (!this.isEnabled()) return;
4380
4474
  this.captureUnhandled(e.error ?? e.message ?? "error");
@@ -4398,6 +4492,9 @@ var CrashReporter = class {
4398
4492
  }
4399
4493
  };
4400
4494
 
4495
+ // src/core/deepdots-popups.ts
4496
+ init_logger();
4497
+
4401
4498
  // src/analytics/messaging.ts
4402
4499
  function buildMessageParams(stage, o) {
4403
4500
  const p = {
@@ -4426,6 +4523,8 @@ var DeepdotsPopups = class {
4426
4523
  this.triggers = [];
4427
4524
  this.initialized = false;
4428
4525
  this.renderer = createDefaultRenderer();
4526
+ /** Logger para el output de debug. Default `console`; el host puede inyectar el suyo en init(). */
4527
+ this.logger = console;
4429
4528
  this.popupContainer = null;
4430
4529
  // deprecated: mantenido para compatibilidad interna
4431
4530
  this.popupDefinitions = [];
@@ -4461,6 +4560,8 @@ var DeepdotsPopups = class {
4461
4560
  }
4462
4561
  /** Initialize the SDK with configuration */
4463
4562
  init(config) {
4563
+ this.logger = config.logger ?? console;
4564
+ setLogger(this.logger);
4464
4565
  if (this.initialized) {
4465
4566
  this.log("SDK already initialized");
4466
4567
  return;
@@ -4500,7 +4601,7 @@ var DeepdotsPopups = class {
4500
4601
  onSessionId: (id) => {
4501
4602
  this.analyticsFeedbackSessionId = id;
4502
4603
  }
4503
- }) : void 0;
4604
+ }) : createDryRunSink((...a) => this.logger.log(...a));
4504
4605
  const device = config.device ?? collectDeviceInfo(config.appVersion);
4505
4606
  this.analytics = new AnalyticsManager({
4506
4607
  sink: analyticsSink,
@@ -5209,7 +5310,8 @@ var DeepdotsPopups = class {
5209
5310
  try {
5210
5311
  listener(event);
5211
5312
  } catch (error) {
5212
- console.error("Error in event listener:", error);
5313
+ if (this.logger.error) this.logger.error("Error in event listener:", error);
5314
+ else this.logger.log("Error in event listener:", error);
5213
5315
  }
5214
5316
  });
5215
5317
  }
@@ -5217,7 +5319,7 @@ var DeepdotsPopups = class {
5217
5319
  /** Log debug messages */
5218
5320
  log(...args) {
5219
5321
  if (this.config?.debug) {
5220
- console.log("[DeepdotsPopups]", ...args);
5322
+ this.logger.log("[DeepdotsPopups]", ...args);
5221
5323
  }
5222
5324
  }
5223
5325
  /** External debug method for triggers */