@gemx-dev/heatmap-react 3.5.92-dev.1 → 3.5.92-dev.10

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.
Files changed (158) hide show
  1. package/dist/esm/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  2. package/dist/esm/hooks/view-context/index.d.ts +1 -0
  3. package/dist/esm/hooks/view-context/index.d.ts.map +1 -1
  4. package/dist/esm/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -1
  5. package/dist/esm/hooks/view-context/useHeatmapDataContext.d.ts +2 -16
  6. package/dist/esm/hooks/view-context/useHeatmapDataContext.d.ts.map +1 -1
  7. package/dist/esm/hooks/view-context/useHeatmapLiveContext.d.ts +34 -0
  8. package/dist/esm/hooks/view-context/useHeatmapLiveContext.d.ts.map +1 -0
  9. package/dist/esm/hooks/view-context/useHeatmapSettingContext.d.ts +3 -1
  10. package/dist/esm/hooks/view-context/useHeatmapSettingContext.d.ts.map +1 -1
  11. package/dist/esm/hooks/view-context/useHeatmapVizContext.d.ts +2 -2
  12. package/dist/esm/hooks/view-context/useHeatmapVizContext.d.ts.map +1 -1
  13. package/dist/esm/hooks/viz-live/useVizLiveIframeMsg.d.ts.map +1 -1
  14. package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
  15. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  16. package/dist/esm/index.d.ts +1 -1
  17. package/dist/esm/index.d.ts.map +1 -1
  18. package/dist/esm/index.js +1144 -611
  19. package/dist/esm/index.mjs +1144 -611
  20. package/dist/esm/libs/iframe-processor/index.d.ts +2 -5
  21. package/dist/esm/libs/iframe-processor/index.d.ts.map +1 -1
  22. package/dist/esm/libs/iframe-processor/lifecycle.d.ts +15 -7
  23. package/dist/esm/libs/iframe-processor/lifecycle.d.ts.map +1 -1
  24. package/dist/esm/libs/iframe-processor/orchestrator.d.ts +13 -45
  25. package/dist/esm/libs/iframe-processor/orchestrator.d.ts.map +1 -1
  26. package/dist/esm/libs/iframe-processor/processors/height-observer/index.d.ts +3 -14
  27. package/dist/esm/libs/iframe-processor/processors/height-observer/index.d.ts.map +1 -1
  28. package/dist/esm/libs/iframe-processor/processors/height-observer/types.d.ts +9 -1
  29. package/dist/esm/libs/iframe-processor/processors/height-observer/types.d.ts.map +1 -1
  30. package/dist/esm/libs/iframe-processor/processors/navigation/index.d.ts +3 -14
  31. package/dist/esm/libs/iframe-processor/processors/navigation/index.d.ts.map +1 -1
  32. package/dist/esm/libs/iframe-processor/processors/navigation/listeners.d.ts +3 -4
  33. package/dist/esm/libs/iframe-processor/processors/navigation/listeners.d.ts.map +1 -1
  34. package/dist/esm/libs/iframe-processor/processors/navigation/types.d.ts +13 -0
  35. package/dist/esm/libs/iframe-processor/processors/navigation/types.d.ts.map +1 -1
  36. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/global-fixes/viewport-unit-replacer/fixes.d.ts.map +1 -1
  37. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/gp-v7-fixes/gem-slider-item/fixes.d.ts.map +1 -1
  38. package/dist/esm/libs/iframe-processor/processors/viewport/global-fixes/types.d.ts +2 -2
  39. package/dist/esm/libs/iframe-processor/processors/viewport/index.d.ts +3 -9
  40. package/dist/esm/libs/iframe-processor/processors/viewport/index.d.ts.map +1 -1
  41. package/dist/esm/libs/iframe-processor/processors/viewport/listeners.d.ts +3 -2
  42. package/dist/esm/libs/iframe-processor/processors/viewport/listeners.d.ts.map +1 -1
  43. package/dist/esm/libs/iframe-processor/processors/viewport/pipeline.d.ts.map +1 -1
  44. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts +2 -0
  45. package/dist/esm/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts.map +1 -1
  46. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/constants.d.ts +5 -0
  47. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/constants.d.ts.map +1 -0
  48. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/functions.d.ts +11 -0
  49. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/functions.d.ts.map +1 -0
  50. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/index.d.ts +9 -0
  51. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/index.d.ts.map +1 -0
  52. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/state.d.ts +13 -0
  53. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/state.d.ts.map +1 -0
  54. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/types.d.ts +16 -0
  55. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer/types.d.ts.map +1 -0
  56. package/dist/esm/libs/iframe-processor/processors/viewport/types.d.ts +8 -0
  57. package/dist/esm/libs/iframe-processor/processors/viewport/types.d.ts.map +1 -0
  58. package/dist/esm/libs/iframe-processor/shared/factory-types.d.ts +24 -0
  59. package/dist/esm/libs/iframe-processor/shared/factory-types.d.ts.map +1 -0
  60. package/dist/esm/libs/iframe-processor/shared/iframe-types.d.ts +20 -0
  61. package/dist/esm/libs/iframe-processor/shared/iframe-types.d.ts.map +1 -0
  62. package/dist/esm/libs/iframe-processor/shared/perf.d.ts +58 -0
  63. package/dist/esm/libs/iframe-processor/shared/perf.d.ts.map +1 -0
  64. package/dist/esm/stores/config.d.ts +0 -2
  65. package/dist/esm/stores/config.d.ts.map +1 -1
  66. package/dist/esm/stores/data.d.ts +2 -0
  67. package/dist/esm/stores/data.d.ts.map +1 -1
  68. package/dist/esm/stores/mode-live.d.ts +30 -16
  69. package/dist/esm/stores/mode-live.d.ts.map +1 -1
  70. package/dist/esm/stores/setting.d.ts +3 -1
  71. package/dist/esm/stores/setting.d.ts.map +1 -1
  72. package/dist/esm/stores/viz.d.ts +2 -2
  73. package/dist/esm/stores/viz.d.ts.map +1 -1
  74. package/dist/esm/types/heatmap.d.ts +4 -0
  75. package/dist/esm/types/heatmap.d.ts.map +1 -1
  76. package/dist/esm/types/iframe-helper.d.ts +0 -19
  77. package/dist/esm/types/iframe-helper.d.ts.map +1 -1
  78. package/dist/umd/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  79. package/dist/umd/hooks/view-context/index.d.ts +1 -0
  80. package/dist/umd/hooks/view-context/index.d.ts.map +1 -1
  81. package/dist/umd/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -1
  82. package/dist/umd/hooks/view-context/useHeatmapDataContext.d.ts +2 -16
  83. package/dist/umd/hooks/view-context/useHeatmapDataContext.d.ts.map +1 -1
  84. package/dist/umd/hooks/view-context/useHeatmapLiveContext.d.ts +34 -0
  85. package/dist/umd/hooks/view-context/useHeatmapLiveContext.d.ts.map +1 -0
  86. package/dist/umd/hooks/view-context/useHeatmapSettingContext.d.ts +3 -1
  87. package/dist/umd/hooks/view-context/useHeatmapSettingContext.d.ts.map +1 -1
  88. package/dist/umd/hooks/view-context/useHeatmapVizContext.d.ts +2 -2
  89. package/dist/umd/hooks/view-context/useHeatmapVizContext.d.ts.map +1 -1
  90. package/dist/umd/hooks/viz-live/useVizLiveIframeMsg.d.ts.map +1 -1
  91. package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
  92. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  93. package/dist/umd/index.d.ts +1 -1
  94. package/dist/umd/index.d.ts.map +1 -1
  95. package/dist/umd/index.js +2 -2
  96. package/dist/umd/libs/iframe-processor/index.d.ts +2 -5
  97. package/dist/umd/libs/iframe-processor/index.d.ts.map +1 -1
  98. package/dist/umd/libs/iframe-processor/lifecycle.d.ts +15 -7
  99. package/dist/umd/libs/iframe-processor/lifecycle.d.ts.map +1 -1
  100. package/dist/umd/libs/iframe-processor/orchestrator.d.ts +13 -45
  101. package/dist/umd/libs/iframe-processor/orchestrator.d.ts.map +1 -1
  102. package/dist/umd/libs/iframe-processor/processors/height-observer/index.d.ts +3 -14
  103. package/dist/umd/libs/iframe-processor/processors/height-observer/index.d.ts.map +1 -1
  104. package/dist/umd/libs/iframe-processor/processors/height-observer/types.d.ts +9 -1
  105. package/dist/umd/libs/iframe-processor/processors/height-observer/types.d.ts.map +1 -1
  106. package/dist/umd/libs/iframe-processor/processors/navigation/index.d.ts +3 -14
  107. package/dist/umd/libs/iframe-processor/processors/navigation/index.d.ts.map +1 -1
  108. package/dist/umd/libs/iframe-processor/processors/navigation/listeners.d.ts +3 -4
  109. package/dist/umd/libs/iframe-processor/processors/navigation/listeners.d.ts.map +1 -1
  110. package/dist/umd/libs/iframe-processor/processors/navigation/types.d.ts +13 -0
  111. package/dist/umd/libs/iframe-processor/processors/navigation/types.d.ts.map +1 -1
  112. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/global-fixes/viewport-unit-replacer/fixes.d.ts.map +1 -1
  113. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/gp-v7-fixes/gem-slider-item/fixes.d.ts.map +1 -1
  114. package/dist/umd/libs/iframe-processor/processors/viewport/global-fixes/types.d.ts +2 -2
  115. package/dist/umd/libs/iframe-processor/processors/viewport/index.d.ts +3 -9
  116. package/dist/umd/libs/iframe-processor/processors/viewport/index.d.ts.map +1 -1
  117. package/dist/umd/libs/iframe-processor/processors/viewport/listeners.d.ts +3 -2
  118. package/dist/umd/libs/iframe-processor/processors/viewport/listeners.d.ts.map +1 -1
  119. package/dist/umd/libs/iframe-processor/processors/viewport/pipeline.d.ts.map +1 -1
  120. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts +2 -0
  121. package/dist/umd/libs/iframe-processor/processors/viewport/shop-overrides/types.d.ts.map +1 -1
  122. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/constants.d.ts +5 -0
  123. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/constants.d.ts.map +1 -0
  124. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/functions.d.ts +11 -0
  125. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/functions.d.ts.map +1 -0
  126. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/index.d.ts +9 -0
  127. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/index.d.ts.map +1 -0
  128. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/state.d.ts +13 -0
  129. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/state.d.ts.map +1 -0
  130. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/types.d.ts +16 -0
  131. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer/types.d.ts.map +1 -0
  132. package/dist/umd/libs/iframe-processor/processors/viewport/types.d.ts +8 -0
  133. package/dist/umd/libs/iframe-processor/processors/viewport/types.d.ts.map +1 -0
  134. package/dist/umd/libs/iframe-processor/shared/factory-types.d.ts +24 -0
  135. package/dist/umd/libs/iframe-processor/shared/factory-types.d.ts.map +1 -0
  136. package/dist/umd/libs/iframe-processor/shared/iframe-types.d.ts +20 -0
  137. package/dist/umd/libs/iframe-processor/shared/iframe-types.d.ts.map +1 -0
  138. package/dist/umd/libs/iframe-processor/shared/perf.d.ts +58 -0
  139. package/dist/umd/libs/iframe-processor/shared/perf.d.ts.map +1 -0
  140. package/dist/umd/stores/config.d.ts +0 -2
  141. package/dist/umd/stores/config.d.ts.map +1 -1
  142. package/dist/umd/stores/data.d.ts +2 -0
  143. package/dist/umd/stores/data.d.ts.map +1 -1
  144. package/dist/umd/stores/mode-live.d.ts +30 -16
  145. package/dist/umd/stores/mode-live.d.ts.map +1 -1
  146. package/dist/umd/stores/setting.d.ts +3 -1
  147. package/dist/umd/stores/setting.d.ts.map +1 -1
  148. package/dist/umd/stores/viz.d.ts +2 -2
  149. package/dist/umd/stores/viz.d.ts.map +1 -1
  150. package/dist/umd/types/heatmap.d.ts +4 -0
  151. package/dist/umd/types/heatmap.d.ts.map +1 -1
  152. package/dist/umd/types/iframe-helper.d.ts +0 -19
  153. package/dist/umd/types/iframe-helper.d.ts.map +1 -1
  154. package/package.json +1 -1
  155. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer.d.ts +0 -54
  156. package/dist/esm/libs/iframe-processor/processors/viewport/style-enforcer.d.ts.map +0 -1
  157. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer.d.ts +0 -54
  158. package/dist/umd/libs/iframe-processor/processors/viewport/style-enforcer.d.ts.map +0 -1
@@ -203,6 +203,11 @@ var EHeatmapMode;
203
203
  EHeatmapMode["Live"] = "live";
204
204
  EHeatmapMode["Compare"] = "compare";
205
205
  })(EHeatmapMode || (EHeatmapMode = {}));
206
+ var EHeatmapDataSource;
207
+ (function (EHeatmapDataSource) {
208
+ EHeatmapDataSource["Snapshot"] = "snapshot";
209
+ EHeatmapDataSource["Live"] = "live";
210
+ })(EHeatmapDataSource || (EHeatmapDataSource = {}));
206
211
 
207
212
  const ViewIdContext = createContext(undefined);
208
213
  const useViewIdContext = () => {
@@ -314,18 +319,16 @@ const useHeatmapConfigStore = create()((set) => {
314
319
  return {
315
320
  mode: EHeatmapMode.Single,
316
321
  sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
317
- clickMode: EClickMode.Default,
318
- isRendering: true,
319
322
  setMode: (mode) => set({ mode }),
320
323
  resetMode: () => set({ mode: EHeatmapMode.Single }),
321
324
  setSidebarWidth: (sidebarWidth) => set({ sidebarWidth }),
322
- setIsRendering: (isRendering) => set({ isRendering }),
323
325
  };
324
326
  });
325
327
 
326
328
  const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
327
329
  return {
328
330
  data: new Map([[DEFAULT_VIEW_ID, undefined]]),
331
+ dataSnapshot: new Map([[DEFAULT_VIEW_ID, undefined]]),
329
332
  clickmap: new Map([[DEFAULT_VIEW_ID, undefined]]),
330
333
  clickAreas: new Map([[DEFAULT_VIEW_ID, undefined]]),
331
334
  dataInfo: new Map([[DEFAULT_VIEW_ID, undefined]]),
@@ -358,6 +361,11 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
358
361
  newData.set(viewId, data);
359
362
  return { data: newData };
360
363
  }),
364
+ setDataSnapshot: (data, viewId = DEFAULT_VIEW_ID) => set((prev) => {
365
+ const newDataSnapshot = new Map(prev.dataSnapshot);
366
+ newDataSnapshot.set(viewId, data);
367
+ return { dataSnapshot: newDataSnapshot };
368
+ }),
361
369
  setClickmap: (clickmap, viewId = DEFAULT_VIEW_ID) => set((prev) => {
362
370
  const newClickmap = new Map(prev.clickmap);
363
371
  newClickmap.set(viewId, clickmap);
@@ -380,12 +388,14 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
380
388
  }),
381
389
  copyView: (fromViewId, toViewId) => set((prev) => {
382
390
  const newData = new Map(prev.data);
391
+ const newDataSnapshot = new Map(prev.dataSnapshot);
383
392
  const newClickmap = new Map(prev.clickmap);
384
393
  const newClickAreas = new Map(prev.clickAreas);
385
394
  const newDataInfo = new Map(prev.dataInfo);
386
395
  const newScrollmap = new Map(prev.scrollmap);
387
396
  const newAttentionMap = new Map(prev.attentionMap);
388
397
  newData.set(toViewId, prev.data.get(fromViewId));
398
+ newDataSnapshot.set(toViewId, prev.dataSnapshot.get(fromViewId));
389
399
  newClickmap.set(toViewId, prev.clickmap.get(fromViewId));
390
400
  newClickAreas.set(toViewId, prev.clickAreas.get(fromViewId));
391
401
  newDataInfo.set(toViewId, prev.dataInfo.get(fromViewId));
@@ -393,6 +403,7 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
393
403
  newAttentionMap.set(toViewId, prev.attentionMap.get(fromViewId));
394
404
  return {
395
405
  data: newData,
406
+ dataSnapshot: newDataSnapshot,
396
407
  clickmap: newClickmap,
397
408
  clickAreas: newClickAreas,
398
409
  dataInfo: newDataInfo,
@@ -402,12 +413,14 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
402
413
  }),
403
414
  clearView: (viewId) => set((prev) => {
404
415
  const newData = new Map(prev.data);
416
+ const newDataSnapshot = new Map(prev.dataSnapshot);
405
417
  const newClickmap = new Map(prev.clickmap);
406
418
  const newClickAreas = new Map(prev.clickAreas);
407
419
  const newDataInfo = new Map(prev.dataInfo);
408
420
  const newScrollmap = new Map(prev.scrollmap);
409
421
  const newAttentionMap = new Map(prev.attentionMap);
410
422
  newData.delete(viewId);
423
+ newDataSnapshot.delete(viewId);
411
424
  newClickmap.delete(viewId);
412
425
  newClickAreas.delete(viewId);
413
426
  newDataInfo.delete(viewId);
@@ -415,6 +428,7 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
415
428
  newAttentionMap.delete(viewId);
416
429
  return {
417
430
  data: newData,
431
+ dataSnapshot: newDataSnapshot,
418
432
  clickmap: newClickmap,
419
433
  clickAreas: newClickAreas,
420
434
  dataInfo: newDataInfo,
@@ -424,6 +438,7 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
424
438
  }),
425
439
  resetAll: () => set({
426
440
  data: new Map([[DEFAULT_VIEW_ID, undefined]]),
441
+ dataSnapshot: new Map([[DEFAULT_VIEW_ID, undefined]]),
427
442
  clickmap: new Map([[DEFAULT_VIEW_ID, undefined]]),
428
443
  clickAreas: new Map([[DEFAULT_VIEW_ID, undefined]]),
429
444
  dataInfo: new Map([[DEFAULT_VIEW_ID, undefined]]),
@@ -445,6 +460,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
445
460
  clickMode: new Map([[DEFAULT_VIEW_ID, EClickMode.Default]]),
446
461
  scrollType: new Map([[DEFAULT_VIEW_ID, EScrollType.Depth]]),
447
462
  heatmapType: new Map([[DEFAULT_VIEW_ID, EHeatmapType.Click]]),
463
+ dataSource: new Map([[DEFAULT_VIEW_ID, EHeatmapDataSource.Snapshot]]),
448
464
  setIsRendering: (isRendering, viewId = DEFAULT_VIEW_ID) => set((prev) => {
449
465
  const newIsRendering = new Map(prev.isRendering);
450
466
  newIsRendering.set(viewId, isRendering);
@@ -495,6 +511,11 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
495
511
  newHeatmapType.set(viewId, heatmapType);
496
512
  return { heatmapType: newHeatmapType };
497
513
  }),
514
+ setDataSource: (dataSource, viewId = DEFAULT_VIEW_ID) => set((prev) => {
515
+ const newDataSource = new Map(prev.dataSource);
516
+ newDataSource.set(viewId, dataSource);
517
+ return { dataSource: newDataSource };
518
+ }),
498
519
  copyView: (fromViewId, toViewId) => set((prev) => {
499
520
  const newIsLoadingDom = new Map(prev.isLoadingDom);
500
521
  const newIsLoadingCanvas = new Map(prev.isLoadingCanvas);
@@ -505,6 +526,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
505
526
  const newClickMode = new Map(prev.clickMode);
506
527
  const newScrollType = new Map(prev.scrollType);
507
528
  const newHeatmapType = new Map(prev.heatmapType);
529
+ const newDataSource = new Map(prev.dataSource);
508
530
  newIsShowSidebar.set(toViewId, prev.isShowSidebar.get(fromViewId) ?? false);
509
531
  newRankedBy.set(toViewId, prev.rankedBy.get(fromViewId));
510
532
  newDeviceType.set(toViewId, prev.deviceType.get(fromViewId));
@@ -514,6 +536,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
514
536
  newHeatmapType.set(toViewId, prev.heatmapType.get(fromViewId));
515
537
  newIsLoadingDom.set(toViewId, prev.isLoadingDom.get(fromViewId) ?? false);
516
538
  newIsLoadingCanvas.set(toViewId, prev.isLoadingCanvas.get(fromViewId) ?? false);
539
+ newDataSource.set(toViewId, prev.dataSource.get(fromViewId) ?? EHeatmapDataSource.Snapshot);
517
540
  return {
518
541
  isShowSidebar: newIsShowSidebar,
519
542
  rankedBy: newRankedBy,
@@ -524,6 +547,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
524
547
  clickMode: newClickMode,
525
548
  scrollType: newScrollType,
526
549
  heatmapType: newHeatmapType,
550
+ dataSource: newDataSource,
527
551
  };
528
552
  }),
529
553
  clearView: (viewId) => set((prev) => {
@@ -536,6 +560,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
536
560
  const newClickMode = new Map(prev.clickMode);
537
561
  const newScrollType = new Map(prev.scrollType);
538
562
  const newHeatmapType = new Map(prev.heatmapType);
563
+ const newDataSource = new Map(prev.dataSource);
539
564
  newIsShowSidebar.delete(viewId);
540
565
  newRankedBy.delete(viewId);
541
566
  newIsLoadingDom.delete(viewId);
@@ -545,6 +570,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
545
570
  newClickMode.delete(viewId);
546
571
  newScrollType.delete(viewId);
547
572
  newHeatmapType.delete(viewId);
573
+ newDataSource.delete(viewId);
548
574
  return {
549
575
  isShowSidebar: newIsShowSidebar,
550
576
  rankedBy: newRankedBy,
@@ -555,6 +581,7 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
555
581
  clickMode: newClickMode,
556
582
  scrollType: newScrollType,
557
583
  heatmapType: newHeatmapType,
584
+ dataSource: newDataSource,
558
585
  };
559
586
  }),
560
587
  resetAll: () => set({
@@ -568,22 +595,23 @@ const useHeatmapSettingStore = create()(subscribeWithSelector((set) => {
568
595
  clickMode: new Map([[DEFAULT_VIEW_ID, EClickMode.Default]]),
569
596
  scrollType: new Map([[DEFAULT_VIEW_ID, EScrollType.Depth]]),
570
597
  heatmapType: new Map([[DEFAULT_VIEW_ID, EHeatmapType.Click]]),
598
+ dataSource: new Map([[DEFAULT_VIEW_ID, EHeatmapDataSource.Snapshot]]),
571
599
  }),
572
600
  };
573
601
  }));
574
602
 
575
603
  const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
576
604
  return {
577
- isRenderViz: new Map([[DEFAULT_VIEW_ID, false]]),
605
+ isRenderedViz: new Map([[DEFAULT_VIEW_ID, false]]),
578
606
  zoomRatio: new Map([[DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO.DEFAULT]]),
579
607
  minZoomRatio: new Map([[DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO.MIN]]),
580
608
  maxZoomRatio: new Map([[DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO.MAX]]),
581
609
  scale: new Map([[DEFAULT_VIEW_ID, 1]]),
582
610
  isScaledToFit: new Map([[DEFAULT_VIEW_ID, false]]),
583
- setIsRenderViz: (isRenderViz, viewId = DEFAULT_VIEW_ID) => set((prev) => {
584
- const newIsRenderViz = new Map(prev.isRenderViz);
585
- newIsRenderViz.set(viewId, isRenderViz);
586
- return { isRenderViz: newIsRenderViz };
611
+ setIsRenderedViz: (isRenderedViz, viewId = DEFAULT_VIEW_ID) => set((prev) => {
612
+ const newIsRenderedViz = new Map(prev.isRenderedViz);
613
+ newIsRenderedViz.set(viewId, isRenderedViz);
614
+ return { isRenderedViz: newIsRenderedViz };
587
615
  }),
588
616
  setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID) => set((prev) => {
589
617
  const newZoomRatio = new Map(prev.zoomRatio);
@@ -611,18 +639,18 @@ const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
611
639
  return { isScaledToFit: newIsScaledToFit };
612
640
  }),
613
641
  copyView: (fromViewId, toViewId) => set((prev) => {
614
- const newIsRenderViz = new Map(prev.isRenderViz);
642
+ const newIsRenderedViz = new Map(prev.isRenderedViz);
615
643
  const newZoomRatio = new Map(prev.zoomRatio);
616
644
  const newMinZoomRatio = new Map(prev.minZoomRatio);
617
645
  const newScale = new Map(prev.scale);
618
646
  const newIsScaledToFit = new Map(prev.isScaledToFit);
619
- newIsRenderViz.set(toViewId, prev.isRenderViz.get(fromViewId) ?? false);
647
+ newIsRenderedViz.set(toViewId, prev.isRenderedViz.get(fromViewId) ?? false);
620
648
  newZoomRatio.set(toViewId, prev.zoomRatio.get(fromViewId) ?? 100);
621
649
  newMinZoomRatio.set(toViewId, prev.minZoomRatio.get(fromViewId) ?? 10);
622
650
  newScale.set(toViewId, prev.scale.get(fromViewId) ?? 1);
623
651
  newIsScaledToFit.set(toViewId, prev.isScaledToFit.get(fromViewId) ?? false);
624
652
  return {
625
- isRenderViz: newIsRenderViz,
653
+ isRenderedViz: newIsRenderedViz,
626
654
  zoomRatio: newZoomRatio,
627
655
  minZoomRatio: newMinZoomRatio,
628
656
  scale: newScale,
@@ -630,18 +658,18 @@ const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
630
658
  };
631
659
  }),
632
660
  clearView: (viewId) => set((prev) => {
633
- const newIsRenderViz = new Map(prev.isRenderViz);
661
+ const newIsRenderedViz = new Map(prev.isRenderedViz);
634
662
  const newZoomRatio = new Map(prev.zoomRatio);
635
663
  const newMinZoomRatio = new Map(prev.minZoomRatio);
636
664
  const newScale = new Map(prev.scale);
637
665
  const newIsScaledToFit = new Map(prev.isScaledToFit);
638
- newIsRenderViz.delete(viewId);
666
+ newIsRenderedViz.delete(viewId);
639
667
  newZoomRatio.delete(viewId);
640
668
  newMinZoomRatio.delete(viewId);
641
669
  newScale.delete(viewId);
642
670
  newIsScaledToFit.delete(viewId);
643
671
  return {
644
- isRenderViz: newIsRenderViz,
672
+ isRenderedViz: newIsRenderedViz,
645
673
  zoomRatio: newZoomRatio,
646
674
  minZoomRatio: newMinZoomRatio,
647
675
  scale: newScale,
@@ -649,7 +677,7 @@ const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
649
677
  };
650
678
  }),
651
679
  resetAll: () => set({
652
- isRenderViz: new Map([[DEFAULT_VIEW_ID, false]]),
680
+ isRenderedViz: new Map([[DEFAULT_VIEW_ID, false]]),
653
681
  zoomRatio: new Map([[DEFAULT_VIEW_ID, 100]]),
654
682
  minZoomRatio: new Map([[DEFAULT_VIEW_ID, 10]]),
655
683
  scale: new Map([[DEFAULT_VIEW_ID, 1]]),
@@ -1047,24 +1075,108 @@ const useHeatmapCompareStore = create()((set, get) => {
1047
1075
  });
1048
1076
 
1049
1077
  const initialState = {
1050
- payloads: [],
1051
- htmlContent: '',
1052
- targetUrl: '',
1053
- renderMode: 'portal',
1054
- storefrontPassword: '',
1055
- };
1056
- const useHeatmapLiveStore = create()((set) => {
1057
- return {
1058
- ...initialState,
1059
- reset: () => set(initialState),
1060
- setPayloads: (payloads) => set({ payloads }),
1061
- addPayload: (payload) => set((state) => ({ payloads: [...state.payloads, payload] })),
1062
- setHtmlContent: (htmlContent) => set({ htmlContent }),
1063
- setTargetUrl: (targetUrl) => set({ targetUrl }),
1064
- setRenderMode: (renderMode) => set({ renderMode }),
1065
- setStorefrontPassword: (storefrontPassword) => set({ storefrontPassword }),
1066
- };
1067
- });
1078
+ decodedPayloads: new Map([[DEFAULT_VIEW_ID, []]]),
1079
+ encodedPayloads: new Map([[DEFAULT_VIEW_ID, []]]),
1080
+ htmlContent: new Map([[DEFAULT_VIEW_ID, '']]),
1081
+ targetUrl: new Map([[DEFAULT_VIEW_ID, '']]),
1082
+ renderMode: new Map([[DEFAULT_VIEW_ID, 'portal']]),
1083
+ storefrontPassword: new Map([[DEFAULT_VIEW_ID, '']]),
1084
+ };
1085
+ const useHeatmapLiveStore = create()(subscribeWithSelector((set) => ({
1086
+ ...initialState,
1087
+ addPayload: (payload, viewId = DEFAULT_VIEW_ID) => set((prev) => {
1088
+ const newDecoded = new Map(prev.decodedPayloads);
1089
+ newDecoded.set(viewId, [...(newDecoded.get(viewId) ?? []), payload]);
1090
+ const newEncoded = new Map(prev.encodedPayloads);
1091
+ newEncoded.set(viewId, [...(newEncoded.get(viewId) ?? []), JSON.stringify(payload)]);
1092
+ return { decodedPayloads: newDecoded, encodedPayloads: newEncoded };
1093
+ }),
1094
+ setPayloads: (payloads, viewId = DEFAULT_VIEW_ID) => set((prev) => {
1095
+ const newDecoded = new Map(prev.decodedPayloads);
1096
+ newDecoded.set(viewId, payloads);
1097
+ const newEncoded = new Map(prev.encodedPayloads);
1098
+ newEncoded.set(viewId, payloads.map((p) => JSON.stringify(p)));
1099
+ return { decodedPayloads: newDecoded, encodedPayloads: newEncoded };
1100
+ }),
1101
+ setEncodedPayloads: (payloads, viewId = DEFAULT_VIEW_ID) => set((prev) => {
1102
+ const newEncoded = new Map(prev.encodedPayloads);
1103
+ newEncoded.set(viewId, payloads);
1104
+ return { encodedPayloads: newEncoded };
1105
+ }),
1106
+ setHtmlContent: (htmlContent, viewId = DEFAULT_VIEW_ID) => set((prev) => {
1107
+ const newHtmlContent = new Map(prev.htmlContent);
1108
+ newHtmlContent.set(viewId, htmlContent);
1109
+ return { htmlContent: newHtmlContent };
1110
+ }),
1111
+ setTargetUrl: (targetUrl, viewId = DEFAULT_VIEW_ID) => set((prev) => {
1112
+ const newTargetUrl = new Map(prev.targetUrl);
1113
+ newTargetUrl.set(viewId, targetUrl);
1114
+ return { targetUrl: newTargetUrl };
1115
+ }),
1116
+ setRenderMode: (renderMode, viewId = DEFAULT_VIEW_ID) => set((prev) => {
1117
+ const newRenderMode = new Map(prev.renderMode);
1118
+ newRenderMode.set(viewId, renderMode);
1119
+ return { renderMode: newRenderMode };
1120
+ }),
1121
+ setStorefrontPassword: (storefrontPassword, viewId = DEFAULT_VIEW_ID) => set((prev) => {
1122
+ const newStorefrontPassword = new Map(prev.storefrontPassword);
1123
+ newStorefrontPassword.set(viewId, storefrontPassword);
1124
+ return { storefrontPassword: newStorefrontPassword };
1125
+ }),
1126
+ resetView: (viewId = DEFAULT_VIEW_ID) => set((prev) => {
1127
+ const newDecoded = new Map(prev.decodedPayloads);
1128
+ newDecoded.set(viewId, []);
1129
+ const newEncoded = new Map(prev.encodedPayloads);
1130
+ newEncoded.set(viewId, []);
1131
+ return { decodedPayloads: newDecoded, encodedPayloads: newEncoded };
1132
+ }),
1133
+ copyView: (fromViewId, toViewId) => set((prev) => {
1134
+ const newDecoded = new Map(prev.decodedPayloads);
1135
+ const newEncoded = new Map(prev.encodedPayloads);
1136
+ const newHtmlContent = new Map(prev.htmlContent);
1137
+ const newTargetUrl = new Map(prev.targetUrl);
1138
+ const newRenderMode = new Map(prev.renderMode);
1139
+ const newStorefrontPassword = new Map(prev.storefrontPassword);
1140
+ newDecoded.set(toViewId, prev.decodedPayloads.get(fromViewId) ?? []);
1141
+ newEncoded.set(toViewId, prev.encodedPayloads.get(fromViewId) ?? []);
1142
+ newHtmlContent.set(toViewId, prev.htmlContent.get(fromViewId) ?? '');
1143
+ newTargetUrl.set(toViewId, prev.targetUrl.get(fromViewId) ?? '');
1144
+ newRenderMode.set(toViewId, prev.renderMode.get(fromViewId) ?? 'portal');
1145
+ newStorefrontPassword.set(toViewId, prev.storefrontPassword.get(fromViewId) ?? '');
1146
+ return {
1147
+ decodedPayloads: newDecoded,
1148
+ encodedPayloads: newEncoded,
1149
+ htmlContent: newHtmlContent,
1150
+ targetUrl: newTargetUrl,
1151
+ renderMode: newRenderMode,
1152
+ storefrontPassword: newStorefrontPassword,
1153
+ };
1154
+ }),
1155
+ clearView: (viewId) => set((prev) => {
1156
+ const newDecoded = new Map(prev.decodedPayloads);
1157
+ const newEncoded = new Map(prev.encodedPayloads);
1158
+ const newHtmlContent = new Map(prev.htmlContent);
1159
+ const newTargetUrl = new Map(prev.targetUrl);
1160
+ const newRenderMode = new Map(prev.renderMode);
1161
+ const newStorefrontPassword = new Map(prev.storefrontPassword);
1162
+ newDecoded.delete(viewId);
1163
+ newEncoded.delete(viewId);
1164
+ newHtmlContent.delete(viewId);
1165
+ newTargetUrl.delete(viewId);
1166
+ newRenderMode.delete(viewId);
1167
+ newStorefrontPassword.delete(viewId);
1168
+ return {
1169
+ decodedPayloads: newDecoded,
1170
+ encodedPayloads: newEncoded,
1171
+ htmlContent: newHtmlContent,
1172
+ targetUrl: newTargetUrl,
1173
+ renderMode: newRenderMode,
1174
+ storefrontPassword: newStorefrontPassword,
1175
+ };
1176
+ }),
1177
+ resetAll: () => set(initialState),
1178
+ reset: () => set(initialState),
1179
+ })));
1068
1180
 
1069
1181
  const useHeatmapVizRectStore = create()(subscribeWithSelector((set) => {
1070
1182
  return {
@@ -1170,26 +1282,11 @@ const useHeatmapClickContext = createViewContextHook({
1170
1282
  }),
1171
1283
  });
1172
1284
 
1173
- /**
1174
- * Hook to access heatmap data state and actions with optional selector
1175
- *
1176
- * @example
1177
- * ```tsx
1178
- * // Get everything
1179
- * const { data, clickmap, setData } = useHeatmapDataContext();
1180
- *
1181
- * // Get only what you need (no unnecessary re-renders)
1182
- * const data = useHeatmapDataContext(s => s.data);
1183
- * const { setData, setClickmap } = useHeatmapDataContext(s => ({
1184
- * setData: s.setData,
1185
- * setClickmap: s.setClickmap,
1186
- * }));
1187
- * ```
1188
- */
1189
1285
  const useHeatmapDataContext = createViewContextHook({
1190
1286
  useStore: useHeatmapDataStore,
1191
1287
  getState: (store, viewId) => ({
1192
1288
  data: store.data.get(viewId),
1289
+ dataSnapshot: store.dataSnapshot.get(viewId),
1193
1290
  clickmap: store.clickmap.get(viewId),
1194
1291
  clickAreas: store.clickAreas.get(viewId),
1195
1292
  scrollmap: store.scrollmap.get(viewId),
@@ -1199,6 +1296,7 @@ const useHeatmapDataContext = createViewContextHook({
1199
1296
  }),
1200
1297
  getActions: (store, viewId) => ({
1201
1298
  setData: (newData) => store.setData(newData, viewId),
1299
+ setDataSnapshot: (newData) => store.setDataSnapshot(newData, viewId),
1202
1300
  setClickmap: (newClickmap) => store.setClickmap(newClickmap, viewId),
1203
1301
  setClickAreas: (newClickAreas) => store.setClickAreas(newClickAreas, viewId),
1204
1302
  setDataInfoByKey: (key, value) => store.setDataInfoByKey(key, value, viewId),
@@ -1221,6 +1319,32 @@ const useHeatmapHoverContext = createViewContextHook({
1221
1319
  }),
1222
1320
  });
1223
1321
 
1322
+ const useHeatmapLiveContext = createViewContextHook({
1323
+ useStore: useHeatmapLiveStore,
1324
+ getState: (store, viewId) => ({
1325
+ decodedPayloads: store.decodedPayloads.get(viewId) ?? [],
1326
+ encodedPayloads: store.encodedPayloads.get(viewId) ?? [],
1327
+ htmlContent: store.htmlContent.get(viewId) ?? '',
1328
+ targetUrl: store.targetUrl.get(viewId) ?? '',
1329
+ renderMode: store.renderMode.get(viewId) ?? 'portal',
1330
+ storefrontPassword: store.storefrontPassword.get(viewId) ?? '',
1331
+ }),
1332
+ getActions: (store, viewId) => ({
1333
+ addPayload: (payload) => store.addPayload(payload, viewId),
1334
+ setPayloads: (payloads) => store.setPayloads(payloads, viewId),
1335
+ setEncodedPayloads: (payloads) => store.setEncodedPayloads(payloads, viewId),
1336
+ setHtmlContent: (htmlContent) => store.setHtmlContent(htmlContent, viewId),
1337
+ setTargetUrl: (targetUrl) => store.setTargetUrl(targetUrl, viewId),
1338
+ setRenderMode: (mode) => store.setRenderMode(mode, viewId),
1339
+ setStorefrontPassword: (password) => store.setStorefrontPassword(password, viewId),
1340
+ resetView: () => store.resetView(viewId),
1341
+ copyView: (fromViewId, toViewId) => store.copyView(fromViewId, toViewId),
1342
+ clearView: (viewId) => store.clearView(viewId),
1343
+ resetAll: () => store.resetAll(),
1344
+ reset: () => store.reset(),
1345
+ }),
1346
+ });
1347
+
1224
1348
  const useHeatmapScrollContext = createViewContextHook({
1225
1349
  useStore: useHeatmapVizScrollStore,
1226
1350
  getState: (store, viewId) => ({
@@ -1250,6 +1374,7 @@ const useHeatmapSettingContext = createViewContextHook({
1250
1374
  clickMode: store.clickMode.get(viewId),
1251
1375
  scrollType: store.scrollType.get(viewId),
1252
1376
  heatmapType: store.heatmapType.get(viewId),
1377
+ dataSource: store.dataSource.get(viewId) ?? EHeatmapDataSource.Snapshot,
1253
1378
  }),
1254
1379
  getActions: (store, viewId) => ({
1255
1380
  setIsShowSidebar: (isShowSidebar) => store.setIsShowSidebar(isShowSidebar, viewId),
@@ -1262,6 +1387,7 @@ const useHeatmapSettingContext = createViewContextHook({
1262
1387
  setIsRendering: (isRendering) => store.setIsRendering(isRendering, viewId),
1263
1388
  setIsLoadingDom: (isLoadingDom) => store.setIsLoadingDom(isLoadingDom, viewId),
1264
1389
  setIsLoadingCanvas: (isLoadingCanvas) => store.setIsLoadingCanvas(isLoadingCanvas, viewId),
1390
+ setDataSource: (dataSource) => store.setDataSource(dataSource, viewId),
1265
1391
  clearView: (viewId) => store.clearView(viewId),
1266
1392
  }),
1267
1393
  });
@@ -1269,7 +1395,7 @@ const useHeatmapSettingContext = createViewContextHook({
1269
1395
  const useHeatmapVizContext = createViewContextHook({
1270
1396
  useStore: useHeatmapVizStore,
1271
1397
  getState: (store, viewId) => ({
1272
- isRenderViz: store.isRenderViz.get(viewId) ?? false,
1398
+ isRenderedViz: store.isRenderedViz.get(viewId) ?? false,
1273
1399
  zoomRatio: store.zoomRatio.get(viewId) ?? DEFAULT_ZOOM_RATIO.DEFAULT,
1274
1400
  minZoomRatio: store.minZoomRatio.get(viewId) ?? DEFAULT_ZOOM_RATIO.MIN,
1275
1401
  maxZoomRatio: store.maxZoomRatio.get(viewId) ?? DEFAULT_ZOOM_RATIO.MAX,
@@ -1277,7 +1403,7 @@ const useHeatmapVizContext = createViewContextHook({
1277
1403
  isScaledToFit: store.isScaledToFit.get(viewId) ?? false,
1278
1404
  }),
1279
1405
  getActions: (store, viewId) => ({
1280
- setIsRenderViz: (value) => store.setIsRenderViz(value, viewId),
1406
+ setIsRenderedViz: (value) => store.setIsRenderedViz(value, viewId),
1281
1407
  setZoomRatio: (value) => store.setZoomRatio(value, viewId),
1282
1408
  setMinZoomRatio: (value) => store.setMinZoomRatio(value, viewId),
1283
1409
  setMaxZoomRatio: (value) => store.setMaxZoomRatio(value, viewId),
@@ -1311,9 +1437,7 @@ const useHeatmapCopyView = () => {
1311
1437
  const copyVizView = useHeatmapVizStore((state) => state.copyView);
1312
1438
  const copyVizClickView = useHeatmapVizClickStore((state) => state.copyView);
1313
1439
  const copyVizAreaClickView = useHeatmapVizClickAreaStore((state) => state.copyView);
1314
- // const copyVizRectView = useHeatmapVizRectStore((state) => state.copyView);
1315
- // const copyVizHoverView = useHeatmapVizHoverStore((state) => state.copyView);
1316
- // const copyVizScrollView = useHeatmapVizScrollStore((state) => state.copyView);
1440
+ const copyLiveView = useHeatmapLiveStore((state) => state.copyView);
1317
1441
  const clearDataView = useHeatmapDataStore((state) => state.clearView);
1318
1442
  const clearSettingView = useHeatmapSettingStore((state) => state.clearView);
1319
1443
  const clearVizView = useHeatmapVizStore((state) => state.clearView);
@@ -1322,6 +1446,7 @@ const useHeatmapCopyView = () => {
1322
1446
  const clearVizHoverView = useHeatmapVizHoverStore((state) => state.clearView);
1323
1447
  const clearVizScrollView = useHeatmapVizScrollStore((state) => state.clearView);
1324
1448
  const clearVizAreaClickView = useHeatmapVizClickAreaStore((state) => state.clearView);
1449
+ const clearLiveView = useHeatmapLiveStore((state) => state.clearView);
1325
1450
  const resetDataAll = useHeatmapDataStore((state) => state.resetAll);
1326
1451
  const resetSettingAll = useHeatmapSettingStore((state) => state.resetAll);
1327
1452
  const resetVizAll = useHeatmapVizStore((state) => state.resetAll);
@@ -1330,15 +1455,14 @@ const useHeatmapCopyView = () => {
1330
1455
  const resetVizHoverAll = useHeatmapVizHoverStore((state) => state.resetAll);
1331
1456
  const resetVizScrollViewAll = useHeatmapVizScrollStore((state) => state.resetAll);
1332
1457
  const resetVizClickAreaAll = useHeatmapVizClickAreaStore((state) => state.resetAll);
1458
+ const resetLiveAll = useHeatmapLiveStore((state) => state.resetAll);
1333
1459
  const copyView = (fromViewId, toViewId) => {
1334
1460
  copyDataView(fromViewId, toViewId);
1335
1461
  copySettingView(fromViewId, toViewId);
1336
1462
  copyVizView(fromViewId, toViewId);
1337
- // copyVizRectView(fromViewId, toViewId);
1338
1463
  copyVizClickView(fromViewId, toViewId);
1339
- // copyVizHoverView(fromViewId, toViewId);
1340
- // copyVizScrollView(fromViewId, toViewId);
1341
1464
  copyVizAreaClickView(fromViewId, toViewId);
1465
+ copyLiveView(fromViewId, toViewId);
1342
1466
  };
1343
1467
  const copyViewToMultiple = (fromViewId, toViewIds) => {
1344
1468
  toViewIds.forEach((toViewId) => {
@@ -1354,6 +1478,7 @@ const useHeatmapCopyView = () => {
1354
1478
  clearVizHoverView(viewId);
1355
1479
  clearVizScrollView(viewId);
1356
1480
  clearVizAreaClickView(viewId);
1481
+ clearLiveView(viewId);
1357
1482
  };
1358
1483
  const clearMultipleViews = (viewIds) => {
1359
1484
  viewIds.forEach((viewId) => {
@@ -1369,6 +1494,7 @@ const useHeatmapCopyView = () => {
1369
1494
  resetVizHoverAll();
1370
1495
  resetVizScrollViewAll();
1371
1496
  resetVizClickAreaAll();
1497
+ resetLiveAll();
1372
1498
  };
1373
1499
  return {
1374
1500
  copyView,
@@ -1770,7 +1896,7 @@ class Logger {
1770
1896
  }
1771
1897
  }
1772
1898
  // Export singleton instance
1773
- const logger$9 = new Logger();
1899
+ const logger$4 = new Logger();
1774
1900
  // Export factory function để tạo logger với config riêng
1775
1901
  function createLogger(config = {}) {
1776
1902
  const instance = new Logger();
@@ -2159,7 +2285,7 @@ function findElementByHash(props) {
2159
2285
  }
2160
2286
  }
2161
2287
  catch (error) {
2162
- logger$9.warn(`Invalid selector "${selector}":`, error);
2288
+ logger$4.warn(`Invalid selector "${selector}":`, error);
2163
2289
  }
2164
2290
  const elementByHash = iframeDocument.querySelector(`[data-clarity-hashalpha="${hash}"], [data-clarity-hash="${hash}"], [data-clarity-hashbeta="${hash}"]`);
2165
2291
  return elementByHash;
@@ -2181,7 +2307,7 @@ function hydrateAreaNode(props) {
2181
2307
  const { id, hash, selector } = persistedData;
2182
2308
  const element = findElementByHash({ hash, selector, iframeDocument, vizRef });
2183
2309
  if (!element) {
2184
- logger$9.warn(`Cannot hydrate area ${id}: element not found for hash ${hash} or selector ${selector}`);
2310
+ logger$4.warn(`Cannot hydrate area ${id}: element not found for hash ${hash} or selector ${selector}`);
2185
2311
  return null;
2186
2312
  }
2187
2313
  const areaNode = buildAreaNode(element, hash, heatmapInfo, shadowRoot, persistedData);
@@ -2198,7 +2324,7 @@ function hydrateAreas(props) {
2198
2324
  hydratedAreas.push(area);
2199
2325
  }
2200
2326
  }
2201
- logger$9.info(`Hydrated ${hydratedAreas.length} of ${clickAreas.length} persisted areas`);
2327
+ logger$4.info(`Hydrated ${hydratedAreas.length} of ${clickAreas.length} persisted areas`);
2202
2328
  return hydratedAreas;
2203
2329
  }
2204
2330
  /**
@@ -2902,16 +3028,16 @@ const calcCalloutPositionAbsolute = (props) => {
2902
3028
 
2903
3029
  function validateAreaCreation(dataInfo, hash, areas) {
2904
3030
  if (!dataInfo?.clickMapMetrics || !dataInfo?.totalClicks) {
2905
- logger$9.warn('Cannot create area: missing heatmap data');
3031
+ logger$4.warn('Cannot create area: missing heatmap data');
2906
3032
  return false;
2907
3033
  }
2908
3034
  if (!hash) {
2909
- logger$9.warn('Cannot create area: missing hash');
3035
+ logger$4.warn('Cannot create area: missing hash');
2910
3036
  return false;
2911
3037
  }
2912
3038
  const alreadyExists = areas.some((area) => area.hash === hash);
2913
3039
  if (alreadyExists) {
2914
- logger$9.warn(`Area already exists for element: ${hash}`);
3040
+ logger$4.warn(`Area already exists for element: ${hash}`);
2915
3041
  return false;
2916
3042
  }
2917
3043
  return true;
@@ -2924,14 +3050,14 @@ function identifyConflictingAreas(area) {
2924
3050
  // Case 1: New area is a child of an existing area
2925
3051
  if (area.parentNode) {
2926
3052
  conflicts.parentId = area.parentNode.id;
2927
- logger$9.info(`New area "${area.selector}" is a child of existing area "${area.parentNode.selector}". Will remove parent.`);
3053
+ logger$4.info(`New area "${area.selector}" is a child of existing area "${area.parentNode.selector}". Will remove parent.`);
2928
3054
  }
2929
3055
  // Case 2: New area is a parent of existing area(s)
2930
3056
  if (area.childNodes.size > 0) {
2931
3057
  area.childNodes.forEach((childArea) => {
2932
3058
  conflicts.childrenIds.push(childArea.id);
2933
3059
  });
2934
- logger$9.info(`New area "${area.selector}" is a parent of ${area.childNodes.size} existing area(s). Will remove children.`);
3060
+ logger$4.info(`New area "${area.selector}" is a parent of ${area.childNodes.size} existing area(s). Will remove children.`);
2935
3061
  }
2936
3062
  return conflicts;
2937
3063
  }
@@ -2982,7 +3108,7 @@ function useAreaCreation(options = {}) {
2982
3108
  }
2983
3109
  }
2984
3110
  catch (error) {
2985
- logger$9.error('Failed to create area:', error);
3111
+ logger$4.error('Failed to create area:', error);
2986
3112
  }
2987
3113
  }, [dataInfo, areas, addArea, removeArea, removeClickArea, customShadowRoot, onAreaCreated]);
2988
3114
  return {
@@ -3097,16 +3223,16 @@ function useAreaHydration(options) {
3097
3223
  return;
3098
3224
  if (!dataInfo)
3099
3225
  return;
3100
- logger$9.info(`Hydrating ${clickAreas.length} persisted areas...`);
3226
+ logger$4.info(`Hydrating ${clickAreas.length} persisted areas...`);
3101
3227
  const hydratedAreas = hydrateAreas({ clickAreas, heatmapInfo: dataInfo, vizRef, shadowRoot });
3102
3228
  if (!hydratedAreas?.length) {
3103
- logger$9.warn('No areas could be hydrated - all elements may have been removed from DOM');
3229
+ logger$4.warn('No areas could be hydrated - all elements may have been removed from DOM');
3104
3230
  return;
3105
3231
  }
3106
3232
  setIsInitializing(true);
3107
3233
  buildAreaGraph(hydratedAreas);
3108
3234
  setAreas(hydratedAreas);
3109
- logger$9.info(`Successfully hydrated ${hydratedAreas.length} areas`);
3235
+ logger$4.info(`Successfully hydrated ${hydratedAreas.length} areas`);
3110
3236
  }, [dataInfo, vizRef, isInitializing, clickAreas]);
3111
3237
  useEffect(() => {
3112
3238
  if (!enabled)
@@ -3240,7 +3366,7 @@ function useAreaRectSync(options) {
3240
3366
  area.rect.update(newRect);
3241
3367
  }
3242
3368
  catch (error) {
3243
- logger$9.error(`Failed to update rect for area ${area.id}:`, error);
3369
+ logger$4.error(`Failed to update rect for area ${area.id}:`, error);
3244
3370
  }
3245
3371
  });
3246
3372
  buildAreaGraph(areas);
@@ -3345,9 +3471,9 @@ const useAreaClickmap = () => {
3345
3471
  const useClickmap = () => {
3346
3472
  const vizRef = useHeatmapVizRectContext((s) => s.vizRef);
3347
3473
  const clickmap = useHeatmapDataContext((s) => s.clickmap);
3348
- const isRenderViz = useHeatmapVizContext((s) => s.isRenderViz);
3474
+ const isRenderedViz = useHeatmapVizContext((s) => s.isRenderedViz);
3349
3475
  const start = useCallback(() => {
3350
- if (!vizRef || !clickmap || clickmap.length === 0 || !isRenderViz)
3476
+ if (!vizRef || !clickmap || clickmap.length === 0 || !isRenderedViz)
3351
3477
  return;
3352
3478
  try {
3353
3479
  vizRef?.clearmap?.();
@@ -3356,7 +3482,7 @@ const useClickmap = () => {
3356
3482
  catch (error) {
3357
3483
  console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
3358
3484
  }
3359
- }, [vizRef, clickmap, isRenderViz]);
3485
+ }, [vizRef, clickmap, isRenderedViz]);
3360
3486
  return { start };
3361
3487
  };
3362
3488
 
@@ -3384,7 +3510,7 @@ const useScrollmap = () => {
3384
3510
  vizRef?.scrollmap?.(scrollmap);
3385
3511
  }
3386
3512
  catch (error) {
3387
- logger$9.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
3513
+ logger$4.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
3388
3514
  }
3389
3515
  }, [vizRef, scrollmap]);
3390
3516
  return { start };
@@ -3855,7 +3981,7 @@ var MessageType;
3855
3981
  })(MessageType || (MessageType = {}));
3856
3982
  function useVizLiveIframeMsg(options = {}) {
3857
3983
  const { trustedOrigins = [], onMessage } = options;
3858
- const addPayload = useHeatmapLiveStore((state) => state.addPayload);
3984
+ const addPayload = useHeatmapLiveContext((s) => s.addPayload);
3859
3985
  const [isReady, setIsReady] = useState(false);
3860
3986
  const iframeRef = useRef(null);
3861
3987
  const isValidOrigin = useCallback((origin) => {
@@ -3881,7 +4007,7 @@ function useVizLiveIframeMsg(options = {}) {
3881
4007
  switch (message.type) {
3882
4008
  case MessageType.GX_DOM_TRACKING_PAYLOAD:
3883
4009
  if (message.payload) {
3884
- const data = decodeClarity(message.payload);
4010
+ const data = JSON.parse(message.payload);
3885
4011
  if (data) {
3886
4012
  addPayload(data);
3887
4013
  }
@@ -3904,21 +4030,324 @@ function useVizLiveIframeMsg(options = {}) {
3904
4030
  };
3905
4031
  }
3906
4032
 
4033
+ /**
4034
+ * Performance tracker — measures render pipeline timings and stores results
4035
+ * in `window.__gemxPerf` for inspection in DevTools.
4036
+ *
4037
+ * Usage:
4038
+ * perf.startSession('render-1');
4039
+ * const t = perf.mark('viewport.run');
4040
+ * perf.measure('viewport.run', t);
4041
+ * perf.endSession();
4042
+ *
4043
+ * // In DevTools:
4044
+ * window.__gemxPerf.latest
4045
+ * window.__gemxPerf.sessions
4046
+ */
4047
+ const s = {
4048
+ enabled: true,
4049
+ current: null,
4050
+ sessions: [],
4051
+ maxSessions: 20,
4052
+ };
4053
+ // ── Functions ─────────────────────────────────────────────────────────────────
4054
+ function startSession(id) {
4055
+ if (!s.enabled)
4056
+ return;
4057
+ s.current = { id, startedAt: performance.now(), entries: [] };
4058
+ }
4059
+ function endSession() {
4060
+ if (!s.enabled || !s.current)
4061
+ return null;
4062
+ const session = s.current;
4063
+ session.total = performance.now() - session.startedAt;
4064
+ s.sessions = [session, ...s.sessions].slice(0, s.maxSessions);
4065
+ s.current = null;
4066
+ flush();
4067
+ return session;
4068
+ }
4069
+ /** Record a point-in-time mark. Returns `performance.now()` for use with measure(). */
4070
+ function mark$1(label) {
4071
+ const now = performance.now();
4072
+ if (s.enabled && s.current) {
4073
+ s.current.entries.push({ label, t: now - s.current.startedAt });
4074
+ }
4075
+ return now;
4076
+ }
4077
+ /** Record a duration from a previous mark() timestamp. */
4078
+ function measure$1(label, t0) {
4079
+ const duration = performance.now() - t0;
4080
+ if (s.enabled && s.current) {
4081
+ s.current.entries.push({ label, t: t0 - s.current.startedAt, duration });
4082
+ }
4083
+ return duration;
4084
+ }
4085
+ function getReport() {
4086
+ return {
4087
+ sessions: s.sessions,
4088
+ latest: s.sessions[0] ?? null,
4089
+ };
4090
+ }
4091
+ function clear$1() {
4092
+ s.current = null;
4093
+ s.sessions = [];
4094
+ if (typeof window !== 'undefined')
4095
+ delete window.__gemxPerf;
4096
+ }
4097
+ function enable$1() {
4098
+ s.enabled = true;
4099
+ }
4100
+ function disable$1() {
4101
+ s.enabled = false;
4102
+ }
4103
+ // ── Internal ──────────────────────────────────────────────────────────────────
4104
+ function flush() {
4105
+ if (typeof window === 'undefined')
4106
+ return;
4107
+ window.__gemxPerf = getReport();
4108
+ }
4109
+ // ── Singleton export ──────────────────────────────────────────────────────────
4110
+ const perf = { startSession, endSession, mark: mark$1, measure: measure$1, getReport, clear: clear$1, enable: enable$1, disable: disable$1 };
4111
+
3907
4112
  /**
3908
4113
  * DOM observation setup — ResizeObserver + MutationObserver.
3909
4114
  * Returns a cleanup function that disconnects both observers.
3910
4115
  */
3911
- createLogger({ enabled: false, prefix: 'IframeHeightObserver' });
4116
+ const logger$3 = createLogger({ enabled: false, prefix: 'IframeHeightObserver' });
4117
+ function setup(doc, onChange) {
4118
+ const resizeObserver = new ResizeObserver(onChange);
4119
+ resizeObserver.observe(doc.documentElement);
4120
+ resizeObserver.observe(doc.body);
4121
+ const mutationObserver = new MutationObserver(onChange);
4122
+ mutationObserver.observe(doc.body, {
4123
+ childList: true,
4124
+ subtree: true,
4125
+ attributes: true,
4126
+ attributeFilter: ['style', 'class', 'hidden', 'data-v'],
4127
+ });
4128
+ logger$3.log('DOM observers started (ResizeObserver + MutationObserver)');
4129
+ return () => {
4130
+ resizeObserver.disconnect();
4131
+ mutationObserver.disconnect();
4132
+ logger$3.log('DOM observers disconnected');
4133
+ };
4134
+ }
3912
4135
 
3913
4136
  /**
3914
4137
  * Height Observer Processor
3915
4138
  * Background observer — watches for iframe content height changes.
3916
4139
  */
3917
- createLogger({ enabled: true, prefix: 'IframeHeightObserver' });
4140
+ // ── Module-level functions ────────────────────────────────────────────────────
4141
+ function clearTimers(s) {
4142
+ if (s.throttleTimeout) {
4143
+ clearTimeout(s.throttleTimeout);
4144
+ s.throttleTimeout = null;
4145
+ }
4146
+ if (s.debounceTimeout) {
4147
+ clearTimeout(s.debounceTimeout);
4148
+ s.debounceTimeout = null;
4149
+ }
4150
+ }
4151
+ function getActualHeight(s) {
4152
+ if (!s.iframe?.contentDocument)
4153
+ return 0;
4154
+ const { documentElement: docEl, body } = s.iframe.contentDocument;
4155
+ const heights = [docEl.scrollHeight, docEl.offsetHeight, body.scrollHeight, body.offsetHeight];
4156
+ const maxHeight = Math.max(...heights.filter((h) => h > 0));
4157
+ s.logger.log('Height sources:', {
4158
+ 'documentElement.scrollHeight': docEl.scrollHeight,
4159
+ 'documentElement.offsetHeight': docEl.offsetHeight,
4160
+ 'body.scrollHeight': body.scrollHeight,
4161
+ 'body.offsetHeight': body.offsetHeight,
4162
+ maxHeight,
4163
+ });
4164
+ return maxHeight;
4165
+ }
4166
+ async function processHeightChange(s, newHeight) {
4167
+ if (!s.iframe || !s.config)
4168
+ return;
4169
+ s.isProcessing = true;
4170
+ s.logger.log(`Processing height change: ${newHeight}px`);
4171
+ try {
4172
+ const result = {
4173
+ height: newHeight,
4174
+ width: s.iframe.contentWindow?.innerWidth ?? 0,
4175
+ };
4176
+ s.lastHeight = newHeight;
4177
+ s.logger.log('Height change processed:', result);
4178
+ s.config.onHeightChange?.(result);
4179
+ window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', { detail: result }));
4180
+ }
4181
+ catch (error) {
4182
+ s.logger.error('Failed to process height change:', error);
4183
+ s.config.onError?.(error);
4184
+ }
4185
+ finally {
4186
+ s.isProcessing = false;
4187
+ }
4188
+ }
4189
+ function handleHeightChange(s) {
4190
+ if (s.isProcessing || s.throttleTimeout)
4191
+ return;
4192
+ s.throttleTimeout = setTimeout(() => {
4193
+ s.throttleTimeout = null;
4194
+ const currentHeight = getActualHeight(s);
4195
+ if (currentHeight === s.lastHeight)
4196
+ return;
4197
+ s.logger.log(`Height changed: ${s.lastHeight}px -> ${currentHeight}px`);
4198
+ if (s.debounceTimeout)
4199
+ clearTimeout(s.debounceTimeout);
4200
+ s.debounceTimeout = setTimeout(() => {
4201
+ s.debounceTimeout = null;
4202
+ processHeightChange(s, currentHeight);
4203
+ }, s.debounceMs);
4204
+ }, s.throttleMs);
4205
+ }
4206
+ function observe(s) {
4207
+ if (!s.iframe?.contentDocument?.body) {
4208
+ s.logger.warn('Cannot observe height changes: iframe body not found');
4209
+ return;
4210
+ }
4211
+ s.observerCleanup?.();
4212
+ s.lastHeight = s.iframe.contentDocument.documentElement.scrollHeight;
4213
+ s.logger.log('Initial height:', s.lastHeight);
4214
+ s.observerCleanup = setup(s.iframe.contentDocument, () => handleHeightChange(s));
4215
+ }
4216
+ function start$5(s, cfg) {
4217
+ if (s.running) {
4218
+ s.logger.warn('Observer is already running. Call stop() first.');
4219
+ return;
4220
+ }
4221
+ s.iframe = cfg.iframe;
4222
+ s.config = cfg;
4223
+ s.throttleMs = cfg.throttleMs ?? 25;
4224
+ s.debounceMs = cfg.debounceMs ?? 500;
4225
+ s.running = true;
4226
+ observe(s);
4227
+ s.logger.log('Height observer started');
4228
+ }
4229
+ function stop$5(s) {
4230
+ if (!s.running)
4231
+ return;
4232
+ s.observerCleanup?.();
4233
+ s.observerCleanup = null;
4234
+ clearTimers(s);
4235
+ s.iframe = null;
4236
+ s.config = null;
4237
+ s.lastHeight = 0;
4238
+ s.isProcessing = false;
4239
+ s.running = false;
4240
+ s.logger.log('Height observer stopped');
4241
+ }
4242
+ function clear(s) {
4243
+ s.observerCleanup?.();
4244
+ s.observerCleanup = null;
4245
+ clearTimers(s);
4246
+ s.lastHeight = 0;
4247
+ s.isProcessing = false;
4248
+ }
4249
+ function updateConfig$3(s, cfg) {
4250
+ if (!s.running || !s.config) {
4251
+ s.logger.warn('Observer is not running.');
4252
+ return;
4253
+ }
4254
+ s.config = { ...s.config, ...cfg };
4255
+ if (cfg.throttleMs !== undefined)
4256
+ s.throttleMs = cfg.throttleMs;
4257
+ if (cfg.debounceMs !== undefined)
4258
+ s.debounceMs = cfg.debounceMs;
4259
+ s.logger.configure({ enabled: !!s.config.debug });
4260
+ s.logger.log('Config updated');
4261
+ }
4262
+ // ── Factory ───────────────────────────────────────────────────────────────────
4263
+ function createHeightObserver() {
4264
+ const s = {
4265
+ logger: createLogger({ enabled: true, prefix: 'IframeHeightObserver' }),
4266
+ iframe: null,
4267
+ config: null,
4268
+ observerCleanup: null,
4269
+ lastHeight: 0,
4270
+ throttleTimeout: null,
4271
+ debounceTimeout: null,
4272
+ isProcessing: false,
4273
+ throttleMs: 25,
4274
+ debounceMs: 500,
4275
+ running: false,
4276
+ };
4277
+ return {
4278
+ start: (cfg) => start$5(s, cfg),
4279
+ stop: () => stop$5(s),
4280
+ observe: () => observe(s),
4281
+ clear: () => clear(s),
4282
+ updateConfig: (cfg) => updateConfig$3(s, cfg),
4283
+ getCurrentHeight: () => s.lastHeight,
4284
+ isRunning: () => s.running,
4285
+ getStateInfo: () => ({
4286
+ isRunning: s.running,
4287
+ lastHeight: s.lastHeight,
4288
+ isProcessing: s.isProcessing,
4289
+ hasObservers: !!s.observerCleanup,
4290
+ }),
4291
+ };
4292
+ }
3918
4293
 
3919
- const logger$8 = createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' });
4294
+ /**
4295
+ * Window-level event management for the navigation processor.
4296
+ *
4297
+ * Responsibilities:
4298
+ * - Subscribe to events dispatched by this processor (for logging/hooks)
4299
+ * - Dispatch navigation events to the parent window
4300
+ */
4301
+ // ── Module-level functions ────────────────────────────────────────────────────
4302
+ function attach$1(s, debug) {
4303
+ s.logger.configure({ enabled: !!debug });
4304
+ s.navigationBlockedListener = (e) => {
4305
+ const ev = e;
4306
+ s.logger.log('Navigation blocked:', ev.detail.url);
4307
+ };
4308
+ s.formSubmitWindowListener = (e) => {
4309
+ const ev = e;
4310
+ s.logger.log('Form submitted:', ev.detail.data);
4311
+ };
4312
+ window.addEventListener('iframe-navigation-blocked', s.navigationBlockedListener);
4313
+ window.addEventListener('iframe-form-submit', s.formSubmitWindowListener);
4314
+ }
4315
+ function detach$1(s) {
4316
+ if (s.navigationBlockedListener) {
4317
+ window.removeEventListener('iframe-navigation-blocked', s.navigationBlockedListener);
4318
+ s.navigationBlockedListener = null;
4319
+ }
4320
+ if (s.formSubmitWindowListener) {
4321
+ window.removeEventListener('iframe-form-submit', s.formSubmitWindowListener);
4322
+ s.formSubmitWindowListener = null;
4323
+ }
4324
+ }
4325
+ function dispatchBlocked(url, showMessage) {
4326
+ if (showMessage)
4327
+ alert(`Navigation blocked: ${url}`);
4328
+ window.dispatchEvent(new CustomEvent('iframe-navigation-blocked', { detail: { url } }));
4329
+ }
4330
+ function dispatchFormSubmit(form, data) {
4331
+ window.dispatchEvent(new CustomEvent('iframe-form-submit', { detail: { form, data } }));
4332
+ }
4333
+ // ── Factory ───────────────────────────────────────────────────────────────────
4334
+ function createNavigationListeners() {
4335
+ const s = {
4336
+ logger: createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' }),
4337
+ navigationBlockedListener: null,
4338
+ formSubmitWindowListener: null,
4339
+ };
4340
+ return {
4341
+ attach: (debug) => attach$1(s, debug),
4342
+ detach: () => detach$1(s),
4343
+ dispatchBlocked,
4344
+ dispatchFormSubmit,
4345
+ };
4346
+ }
4347
+
4348
+ const logger$2 = createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' });
3920
4349
  function configure$1(debug) {
3921
- logger$8.configure({ enabled: debug });
4350
+ logger$2.configure({ enabled: debug });
3922
4351
  }
3923
4352
  // ─── DOM Utilities ────────────────────────────────────────────────────────────
3924
4353
  function disableAllLinks(doc) {
@@ -3942,10 +4371,10 @@ function setupLinkBlocker(doc, isEnabled, onBlocked) {
3942
4371
  return;
3943
4372
  const href = link.getAttribute('href');
3944
4373
  if (!href || href === '' || href === '#' || href.startsWith('#')) {
3945
- logger$8.log('Allowed hash navigation:', href);
4374
+ logger$2.log('Allowed hash navigation:', href);
3946
4375
  return;
3947
4376
  }
3948
- logger$8.log('Blocked link navigation to:', href);
4377
+ logger$2.log('Blocked link navigation to:', href);
3949
4378
  e.preventDefault();
3950
4379
  e.stopPropagation();
3951
4380
  e.stopImmediatePropagation();
@@ -3959,7 +4388,7 @@ function setupLinkBlocker(doc, isEnabled, onBlocked) {
3959
4388
  return;
3960
4389
  const href = link.getAttribute('href');
3961
4390
  if (href && !href.startsWith('#')) {
3962
- logger$8.log('Blocked auxclick navigation');
4391
+ logger$2.log('Blocked auxclick navigation');
3963
4392
  e.preventDefault();
3964
4393
  e.stopPropagation();
3965
4394
  e.stopImmediatePropagation();
@@ -3980,7 +4409,7 @@ function setupFormBlocker(doc, isEnabled, onBlocked, onFormSubmit) {
3980
4409
  const form = e.target;
3981
4410
  const action = form.getAttribute('action');
3982
4411
  if (!action || action === '' || action === '#') {
3983
- logger$8.log('Allowed same-page form');
4412
+ logger$2.log('Allowed same-page form');
3984
4413
  e.preventDefault();
3985
4414
  const data = {};
3986
4415
  new FormData(form).forEach((value, key) => {
@@ -3989,7 +4418,7 @@ function setupFormBlocker(doc, isEnabled, onBlocked, onFormSubmit) {
3989
4418
  onFormSubmit(form, data);
3990
4419
  return;
3991
4420
  }
3992
- logger$8.log('Blocked form submission to:', action);
4421
+ logger$2.log('Blocked form submission to:', action);
3993
4422
  e.preventDefault();
3994
4423
  e.stopPropagation();
3995
4424
  e.stopImmediatePropagation();
@@ -4003,7 +4432,7 @@ function setupWindowOpenBlocker(win, originalOpen, isEnabled, onBlocked) {
4003
4432
  if (!isEnabled())
4004
4433
  return originalOpen(...args);
4005
4434
  const url = args[0]?.toString() || 'popup';
4006
- logger$8.log('Blocked window.open:', url);
4435
+ logger$2.log('Blocked window.open:', url);
4007
4436
  onBlocked(url);
4008
4437
  return null;
4009
4438
  });
@@ -4015,14 +4444,14 @@ function setupUnloadBlocker(win, isEnabled) {
4015
4444
  const beforeUnloadListener = (e) => {
4016
4445
  if (!isEnabled())
4017
4446
  return;
4018
- logger$8.log('Blocked beforeunload');
4447
+ logger$2.log('Blocked beforeunload');
4019
4448
  e.preventDefault();
4020
4449
  e.returnValue = '';
4021
4450
  };
4022
4451
  const unloadListener = (e) => {
4023
4452
  if (!isEnabled())
4024
4453
  return;
4025
- logger$8.log('Blocked unload');
4454
+ logger$2.log('Blocked unload');
4026
4455
  e.preventDefault();
4027
4456
  e.stopPropagation();
4028
4457
  };
@@ -4039,65 +4468,14 @@ function setupDOMMonitor(doc) {
4039
4468
  return () => observer.disconnect();
4040
4469
  }
4041
4470
 
4042
- /**
4043
- * Window-level event management for the navigation processor.
4044
- *
4045
- * Responsibilities:
4046
- * - Subscribe to events dispatched by this processor (for logging/hooks)
4047
- * - Dispatch navigation events to the parent window
4048
- */
4049
- const logger$7 = createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' });
4050
- // ─── State ────────────────────────────────────────────────────────────────────
4051
- let navigationBlockedListener = null;
4052
- let formSubmitWindowListener = null;
4053
- // ─── Subscriptions ────────────────────────────────────────────────────────────
4054
- function attach$1(debug) {
4055
- logger$7.configure({ enabled: !!debug });
4056
- navigationBlockedListener = (e) => {
4057
- const ev = e;
4058
- logger$7.log('Navigation blocked:', ev.detail.url);
4059
- };
4060
- formSubmitWindowListener = (e) => {
4061
- const ev = e;
4062
- logger$7.log('Form submitted:', ev.detail.data);
4063
- };
4064
- window.addEventListener('iframe-navigation-blocked', navigationBlockedListener);
4065
- window.addEventListener('iframe-form-submit', formSubmitWindowListener);
4066
- }
4067
- function detach$1() {
4068
- if (navigationBlockedListener) {
4069
- window.removeEventListener('iframe-navigation-blocked', navigationBlockedListener);
4070
- navigationBlockedListener = null;
4071
- }
4072
- if (formSubmitWindowListener) {
4073
- window.removeEventListener('iframe-form-submit', formSubmitWindowListener);
4074
- formSubmitWindowListener = null;
4075
- }
4076
- }
4077
- // ─── Dispatchers ─────────────────────────────────────────────────────────────
4078
- function dispatchBlocked(url, showMessage) {
4079
- if (showMessage)
4080
- alert(`Navigation blocked: ${url}`);
4081
- window.dispatchEvent(new CustomEvent('iframe-navigation-blocked', { detail: { url } }));
4082
- }
4083
- function dispatchFormSubmit(form, data) {
4084
- window.dispatchEvent(new CustomEvent('iframe-form-submit', { detail: { form, data } }));
4085
- }
4086
-
4087
4471
  /**
4088
4472
  * Navigation Processor
4089
4473
  * Continuous guard — blocks all navigation attempts within the iframe.
4090
4474
  */
4091
- const logger$6 = createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' });
4092
- // ─── State ────────────────────────────────────────────────────────────────────
4093
- let isEnabled = false;
4094
- let showMessage = false;
4095
- let running$4 = false;
4096
- let cleanups = [];
4097
- // ─── Public API ───────────────────────────────────────────────────────────────
4098
- function start$5(iframe, cfg) {
4099
- if (running$4) {
4100
- logger$6.warn('Blocker is already running. Call stop() first.');
4475
+ // ── Module-level functions ────────────────────────────────────────────────────
4476
+ function start$4(s, iframe, cfg) {
4477
+ if (s.running) {
4478
+ s.logger.warn('Blocker is already running. Call stop() first.');
4101
4479
  return;
4102
4480
  }
4103
4481
  if (!iframe.contentDocument || !iframe.contentWindow) {
@@ -4106,76 +4484,114 @@ function start$5(iframe, cfg) {
4106
4484
  const doc = iframe.contentDocument;
4107
4485
  const win = iframe.contentWindow;
4108
4486
  const originalOpen = win.open.bind(win);
4109
- logger$6.configure({ enabled: !!cfg?.debug });
4487
+ s.logger.configure({ enabled: !!cfg?.debug });
4110
4488
  configure$1(!!cfg?.debug);
4111
- cleanups = [
4112
- setupLinkBlocker(doc, () => isEnabled, (url) => dispatchBlocked(url, showMessage)),
4113
- setupFormBlocker(doc, () => isEnabled, (url) => dispatchBlocked(url, showMessage), dispatchFormSubmit),
4114
- setupWindowOpenBlocker(win, originalOpen, () => isEnabled, (url) => dispatchBlocked(url, showMessage)),
4115
- setupUnloadBlocker(win, () => isEnabled),
4489
+ s.cleanups = [
4490
+ setupLinkBlocker(doc, () => s.isEnabled, (url) => s.listeners.dispatchBlocked(url, s.showMessage)),
4491
+ setupFormBlocker(doc, () => s.isEnabled, (url) => s.listeners.dispatchBlocked(url, s.showMessage), s.listeners.dispatchFormSubmit),
4492
+ setupWindowOpenBlocker(win, originalOpen, () => s.isEnabled, (url) => s.listeners.dispatchBlocked(url, s.showMessage)),
4493
+ setupUnloadBlocker(win, () => s.isEnabled),
4116
4494
  setupDOMMonitor(doc),
4117
4495
  ];
4118
- attach$1(cfg?.debug);
4119
- running$4 = true;
4120
- logger$6.log('Navigation blocker started');
4496
+ s.listeners.attach(cfg?.debug);
4497
+ s.running = true;
4498
+ s.logger.log('Navigation blocker started');
4499
+ }
4500
+ function stop$4(s) {
4501
+ if (!s.running)
4502
+ return;
4503
+ s.cleanups.forEach((fn) => fn());
4504
+ s.cleanups = [];
4505
+ s.listeners.detach();
4506
+ s.isEnabled = false;
4507
+ s.showMessage = false;
4508
+ s.running = false;
4509
+ s.logger.log('Navigation blocker stopped');
4510
+ }
4511
+ function enable(s) {
4512
+ if (!s.running) {
4513
+ s.logger.warn('Blocker is not running.');
4514
+ return;
4515
+ }
4516
+ s.isEnabled = true;
4517
+ s.logger.log('Navigation blocking enabled');
4518
+ }
4519
+ function disable(s) {
4520
+ if (!s.running) {
4521
+ s.logger.warn('Blocker is not running.');
4522
+ return;
4523
+ }
4524
+ s.isEnabled = false;
4525
+ s.logger.log('Navigation blocking disabled');
4121
4526
  }
4122
- function stop$5() {
4123
- if (!running$4)
4527
+ function enableMessage(s) {
4528
+ if (!s.running) {
4529
+ s.logger.warn('Blocker is not running.');
4124
4530
  return;
4125
- cleanups.forEach((fn) => fn());
4126
- cleanups = [];
4127
- detach$1();
4128
- isEnabled = false;
4129
- showMessage = false;
4130
- running$4 = false;
4131
- logger$6.log('Navigation blocker stopped');
4132
- }
4133
- function enable() {
4134
- if (!running$4) {
4135
- logger$6.warn('Blocker is not running. Call start() first.');
4531
+ }
4532
+ s.showMessage = true;
4533
+ s.logger.log('Navigation blocking message enabled');
4534
+ }
4535
+ function disableMessage(s) {
4536
+ if (!s.running) {
4537
+ s.logger.warn('Blocker is not running.');
4136
4538
  return;
4137
4539
  }
4138
- isEnabled = true;
4139
- logger$6.log('Navigation blocking enabled');
4540
+ s.showMessage = false;
4541
+ s.logger.log('Navigation blocking message disabled');
4542
+ }
4543
+ // ── Factory ───────────────────────────────────────────────────────────────────
4544
+ function createNavigationBlocker() {
4545
+ const s = {
4546
+ logger: createLogger({ enabled: false, prefix: 'IframeNavigationBlocker' }),
4547
+ listeners: createNavigationListeners(),
4548
+ isEnabled: false,
4549
+ showMessage: false,
4550
+ running: false,
4551
+ cleanups: [],
4552
+ };
4553
+ return {
4554
+ start: (iframe, cfg) => start$4(s, iframe, cfg),
4555
+ stop: () => stop$4(s),
4556
+ enable: () => enable(s),
4557
+ disable: () => disable(s),
4558
+ enableMessage: () => enableMessage(s),
4559
+ disableMessage: () => disableMessage(s),
4560
+ isRunning: () => s.running,
4561
+ isBlockingEnabled: () => s.isEnabled,
4562
+ getStateInfo: () => ({ isRunning: s.running, isEnabled: s.isEnabled, showMessage: s.showMessage }),
4563
+ };
4140
4564
  }
4141
4565
 
4142
- const registry$1 = [];
4143
- /**
4144
- * Register a global fix.
4145
- * Fixes are run in registration order.
4146
- */
4147
- function register$1(fix) {
4148
- registry$1.push(fix);
4566
+ // ── Module-level functions ────────────────────────────────────────────────────
4567
+ function attach(s, debug) {
4568
+ s.logger.configure({ enabled: !!debug });
4569
+ s.dimensionsListener = (e) => {
4570
+ const ev = e;
4571
+ s.logger.log('Dimensions applied:', ev.detail);
4572
+ };
4573
+ window.addEventListener('iframe-dimensions-applied', s.dimensionsListener);
4149
4574
  }
4150
- /**
4151
- * Returns all fixes that are active for the given context.
4152
- * A fix is active if it has no `shouldApply`, or `shouldApply` returns true.
4153
- */
4154
- function getActiveFixes(ctx) {
4155
- return registry$1.filter((fix) => {
4156
- try {
4157
- return !fix.shouldApply || fix.shouldApply(ctx);
4158
- }
4159
- catch {
4160
- return false;
4161
- }
4162
- });
4575
+ function detach(s) {
4576
+ if (s.dimensionsListener) {
4577
+ window.removeEventListener('iframe-dimensions-applied', s.dimensionsListener);
4578
+ s.dimensionsListener = null;
4579
+ }
4580
+ }
4581
+ // ── Factory ───────────────────────────────────────────────────────────────────
4582
+ function createViewportListeners() {
4583
+ const s = {
4584
+ logger: createLogger({ enabled: false, prefix: 'ViewportReplacer' }),
4585
+ dimensionsListener: null,
4586
+ };
4587
+ return {
4588
+ attach: (debug) => attach(s, debug),
4589
+ detach: () => detach(s),
4590
+ };
4163
4591
  }
4164
4592
 
4165
- /**
4166
- * Computed Style Enforcer Module
4167
- * Enforces computed CSS styles with viewport unit verification
4168
- * @module computed-style-enforcer
4169
- */
4170
- const logger$5 = createLogger({
4171
- enabled: false,
4172
- prefix: 'ComputedStyleEnforcer',
4173
- });
4174
- // ============================================================================
4175
- // Constants
4176
- // ============================================================================
4177
4593
  const DEFAULT_TOLERANCE_PX = 5;
4178
- const VIEWPORT_UNIT_REGEX = /([-.\d]+)(vh|svh|lvh|dvh|%)/gi;
4594
+ const VIEWPORT_UNIT_REGEX = /([-.\\d]+)(vh|svh|lvh|dvh|%)/gi;
4179
4595
  const DEFAULT_CSS_VALUES = ['none', 'auto', 'normal', '0px'];
4180
4596
  const CRITICAL_PROPERTIES = [
4181
4597
  'display',
@@ -4212,235 +4628,241 @@ const CRITICAL_PROPERTIES = [
4212
4628
  'grid-template-rows',
4213
4629
  'gap',
4214
4630
  ];
4215
- // ============================================================================
4216
- // State
4217
- // ============================================================================
4218
- let doc$1 = null;
4219
- let win$1 = null;
4220
- let config$2 = null;
4221
- const elementsWithViewportUnits$1 = new Set();
4222
- let originalValues$1 = new WeakMap();
4223
- let running$3 = false;
4224
- // ============================================================================
4225
- // Helper Functions
4226
- // ============================================================================
4227
- /**
4228
- * Get viewport unit map for conversion from current state
4229
- */
4230
- function getViewportUnitMap() {
4231
- if (!config$2) {
4631
+
4632
+ // ── Helpers ───────────────────────────────────────────────────────────────────
4633
+ function getViewportUnitMap(s) {
4634
+ if (!s.config)
4232
4635
  throw new Error('Config is not initialized');
4233
- }
4234
4636
  return {
4235
- vh: config$2.targetHeight,
4236
- svh: config$2.targetHeight,
4237
- lvh: config$2.targetHeight,
4238
- dvh: config$2.targetHeight,
4239
- vw: config$2.targetWidth,
4240
- svw: config$2.targetWidth,
4241
- lvw: config$2.targetWidth,
4242
- dvw: config$2.targetWidth,
4637
+ vh: s.config.targetHeight,
4638
+ svh: s.config.targetHeight,
4639
+ lvh: s.config.targetHeight,
4640
+ dvh: s.config.targetHeight,
4641
+ vw: s.config.targetWidth,
4642
+ svw: s.config.targetWidth,
4643
+ lvw: s.config.targetWidth,
4644
+ dvw: s.config.targetWidth,
4243
4645
  };
4244
4646
  }
4245
- /**
4246
- * Calculate expected pixel value from viewport unit using current state
4247
- */
4248
- function calculateExpectedPx(value, unit) {
4249
- if (!config$2) {
4647
+ function calculateExpectedPx(s, value, unit) {
4648
+ if (!s.config)
4250
4649
  throw new Error('Config is not initialized');
4251
- }
4252
4650
  const unitLower = unit.toLowerCase();
4253
- if (unitLower === '%') {
4254
- return (value / 100) * config$2.targetHeight;
4255
- }
4256
- const unitMap = getViewportUnitMap();
4257
- return (value / 100) * (unitMap[unitLower] || 0);
4651
+ if (unitLower === '%')
4652
+ return (value / 100) * s.config.targetHeight;
4653
+ return (value / 100) * (getViewportUnitMap(s)[unitLower] || 0);
4258
4654
  }
4259
- /**
4260
- * Check if a CSS value is a default/initial value that should be skipped
4261
- */
4262
4655
  function isDefaultCssValue(value) {
4263
4656
  return DEFAULT_CSS_VALUES.includes(value);
4264
4657
  }
4265
- /**
4266
- * Verify if we should replace the computed value using current state
4267
- * Return true ONLY if computed value matches target config (within tolerance)
4268
- * Return false if computed value is different - don't override correct values
4269
- */
4270
- function shouldReplaceValue(computedValue, originalValue, tolerance = DEFAULT_TOLERANCE_PX) {
4271
- if (!config$2) {
4658
+ function shouldReplaceValue(s, computedValue, originalValue, tolerance = DEFAULT_TOLERANCE_PX) {
4659
+ if (!s.config)
4272
4660
  return false;
4273
- }
4274
- // Parse computed value (should be in px)
4275
4661
  const computedPx = parseFloat(computedValue);
4276
- if (isNaN(computedPx)) {
4277
- return false; // Cannot verify, don't replace
4278
- }
4279
- // Parse original value to check what it should be
4662
+ if (isNaN(computedPx))
4663
+ return false;
4280
4664
  const regex = new RegExp(VIEWPORT_UNIT_REGEX.source, VIEWPORT_UNIT_REGEX.flags);
4281
4665
  const match = originalValue.match(regex);
4282
- if (!match) {
4283
- return false; // No viewport units found, don't replace
4284
- }
4666
+ if (!match)
4667
+ return false;
4285
4668
  const [, value, unit] = match;
4286
4669
  const num = parseFloat(value);
4287
- if (isNaN(num)) {
4670
+ if (isNaN(num))
4288
4671
  return false;
4289
- }
4290
- // Calculate expected value based on unit and target config
4291
- const expectedPx = calculateExpectedPx(num, unit);
4292
- // Check if computed value matches expected value (within tolerance)
4672
+ const expectedPx = calculateExpectedPx(s, num, unit);
4293
4673
  const diff = Math.abs(computedPx - expectedPx);
4294
4674
  if (diff <= tolerance) {
4295
- // Matches target config, OK to replace
4296
- logger$5.log(`OK to replace: computed=${computedValue} matches expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px)`);
4675
+ s.logger.log(`OK to replace: computed=${computedValue} matches expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px)`);
4297
4676
  return true;
4298
4677
  }
4299
- // Different from target config, DON'T replace - value already has correct computation
4300
- logger$5.log(`Skip replace: computed=${computedValue} differs from expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px) - keeping current value`);
4678
+ s.logger.log(`Skip replace: computed=${computedValue} differs from expected=${expectedPx.toFixed(2)}px (diff=${diff.toFixed(2)}px)`);
4301
4679
  return false;
4302
4680
  }
4303
- /**
4304
- * Apply a single property to an element with verification using current state
4305
- */
4306
- function applyPropertyWithVerification(element, prop, computedValue, originalValue, tolerance = DEFAULT_TOLERANCE_PX) {
4307
- // Verify before replacing - only replace if computed value matches target config
4308
- if (originalValue && shouldReplaceValue(computedValue, originalValue, tolerance)) {
4681
+ function applyPropertyWithVerification(s, element, prop, computedValue, originalValue, tolerance = DEFAULT_TOLERANCE_PX) {
4682
+ if (originalValue && shouldReplaceValue(s, computedValue, originalValue, tolerance)) {
4309
4683
  element.style.setProperty(prop, computedValue, 'important');
4310
4684
  return true;
4311
4685
  }
4312
4686
  else if (!originalValue) {
4313
- // No original value tracked, use old behavior
4314
4687
  element.style.setProperty(prop, computedValue, 'important');
4315
4688
  return true;
4316
4689
  }
4317
4690
  return false;
4318
4691
  }
4319
- // ============================================================================
4320
- // Public API Functions
4321
- // ============================================================================
4322
- /**
4323
- * Start the computed style enforcer
4324
- * Initialize with iframe document and config
4325
- */
4326
- function start$4(d, w, cfg, options = {}) {
4327
- if (running$3) {
4328
- logger$5.warn('Enforcer is already running. Call stop() first.');
4692
+ // ── Exported module-level functions ───────────────────────────────────────────
4693
+ function start$3(s, d, w, cfg, options = {}) {
4694
+ if (s.running) {
4695
+ s.logger.warn('Enforcer is already running. Call stop() first.');
4329
4696
  return;
4330
4697
  }
4331
- doc$1 = d;
4332
- win$1 = w;
4333
- config$2 = cfg;
4334
- running$3 = true;
4335
- logger$5.configure({ enabled: !!options.debug });
4336
- logger$5.log('Computed style enforcer started');
4698
+ s.doc = d;
4699
+ s.win = w;
4700
+ s.config = cfg;
4701
+ s.running = true;
4702
+ s.logger.configure({ enabled: !!options.debug });
4703
+ s.logger.log('Computed style enforcer started');
4337
4704
  }
4338
- /**
4339
- * Stop the enforcer and clear state
4340
- */
4341
- function stop$4() {
4342
- if (!running$3) {
4705
+ function stop$3(s) {
4706
+ if (!s.running)
4707
+ return;
4708
+ s.doc = null;
4709
+ s.win = null;
4710
+ s.config = null;
4711
+ s.elementsWithViewportUnits.clear();
4712
+ s.originalValues = new WeakMap();
4713
+ s.running = false;
4714
+ s.logger.log('Computed style enforcer stopped');
4715
+ }
4716
+ function reset(s) {
4717
+ if (!s.running) {
4718
+ s.logger.warn('Enforcer is not running. Call start() first.');
4343
4719
  return;
4344
4720
  }
4345
- doc$1 = null;
4346
- win$1 = null;
4347
- config$2 = null;
4348
- elementsWithViewportUnits$1.clear();
4349
- originalValues$1 = new WeakMap();
4350
- running$3 = false;
4351
- logger$5.log('Computed style enforcer stopped');
4721
+ s.elementsWithViewportUnits.clear();
4722
+ s.originalValues = new WeakMap();
4723
+ s.logger.log('Computed style enforcer reset');
4352
4724
  }
4353
- /**
4354
- * Track an element with viewport units
4355
- * Store original CSS values for later verification
4356
- */
4357
- function trackElement(element, propertyOriginalValues) {
4358
- if (!running$3) {
4359
- logger$5.warn('Enforcer is not running. Call start() first.');
4725
+ function trackElement(s, element, propertyOriginalValues) {
4726
+ if (!s.running) {
4727
+ s.logger.warn('Enforcer is not running. Call start() first.');
4360
4728
  return;
4361
4729
  }
4362
- elementsWithViewportUnits$1.add(element);
4363
- // Store original values for this element
4364
- let elementOriginals = originalValues$1.get(element);
4730
+ s.elementsWithViewportUnits.add(element);
4731
+ let elementOriginals = s.originalValues.get(element);
4365
4732
  if (!elementOriginals) {
4366
4733
  elementOriginals = new Map();
4367
- originalValues$1.set(element, elementOriginals);
4734
+ s.originalValues.set(element, elementOriginals);
4368
4735
  }
4369
- // Merge property original values (don't override existing)
4370
4736
  propertyOriginalValues.forEach((value, prop) => {
4371
- if (!elementOriginals.has(prop)) {
4737
+ if (!elementOriginals.has(prop))
4372
4738
  elementOriginals.set(prop, value);
4373
- }
4374
4739
  });
4375
4740
  }
4376
- /**
4377
- * Process a single element - enforce computed styles to inline
4378
- */
4379
- function processElement(element, options = {}) {
4380
- if (!running$3 || !doc$1 || !win$1 || !config$2) {
4381
- logger$5.warn('Enforcer is not running. Call start() first.');
4741
+ function processElement(s, element, options = {}) {
4742
+ if (!s.running || !s.doc || !s.win || !s.config) {
4743
+ s.logger.warn('Enforcer is not running.');
4382
4744
  return 0;
4383
4745
  }
4384
- if (!elementsWithViewportUnits$1.has(element)) {
4746
+ if (!s.elementsWithViewportUnits.has(element))
4385
4747
  return 0;
4386
- }
4387
4748
  const htmlElement = element;
4388
- const computed = win$1.getComputedStyle(htmlElement);
4749
+ const computed = s.win.getComputedStyle(htmlElement);
4389
4750
  const inlineStyle = htmlElement.style;
4390
- const elementOriginals = originalValues$1.get(element);
4751
+ const elementOriginals = s.originalValues.get(element);
4391
4752
  const tolerance = options.tolerance ?? DEFAULT_TOLERANCE_PX;
4392
4753
  let count = 0;
4393
4754
  CRITICAL_PROPERTIES.forEach((prop) => {
4394
4755
  const computedValue = computed.getPropertyValue(prop);
4395
4756
  const inlineValue = inlineStyle.getPropertyValue(prop);
4396
- if (computedValue && (!inlineValue || inlineValue !== computedValue)) {
4397
- if (!isDefaultCssValue(computedValue)) {
4398
- const originalValue = elementOriginals?.get(prop) || '';
4399
- if (applyPropertyWithVerification(htmlElement, prop, computedValue, originalValue, tolerance)) {
4400
- count++;
4401
- }
4402
- }
4757
+ if (computedValue && (!inlineValue || inlineValue !== computedValue) && !isDefaultCssValue(computedValue)) {
4758
+ const originalValue = elementOriginals?.get(prop) || '';
4759
+ if (applyPropertyWithVerification(s, htmlElement, prop, computedValue, originalValue, tolerance))
4760
+ count++;
4403
4761
  }
4404
4762
  });
4405
4763
  return count;
4406
4764
  }
4407
- /**
4408
- * Process all tracked elements
4409
- * Enforce computed styles to inline for all elements with viewport units
4410
- */
4411
- function processAll(options = {}) {
4412
- if (!running$3 || !doc$1 || !win$1 || !config$2) {
4413
- logger$5.warn('Enforcer is not running. Call start() first.');
4765
+ function processAll(s, options = {}) {
4766
+ if (!s.running || !s.doc || !s.win || !s.config) {
4767
+ s.logger.warn('Enforcer is not running.');
4414
4768
  return 0;
4415
4769
  }
4416
4770
  let totalCount = 0;
4417
- elementsWithViewportUnits$1.forEach((element) => {
4418
- totalCount += processElement(element, options);
4771
+ s.elementsWithViewportUnits.forEach((element) => {
4772
+ totalCount += processElement(s, element, options);
4419
4773
  });
4420
- logger$5.log(`Enforced ${totalCount} computed styles for ${elementsWithViewportUnits$1.size} elements`);
4774
+ s.logger.log(`Enforced ${totalCount} computed styles for ${s.elementsWithViewportUnits.size} elements`);
4421
4775
  return totalCount;
4422
4776
  }
4777
+ function updateConfig$2(s, cfg) {
4778
+ if (!s.running || !s.config) {
4779
+ s.logger.warn('Enforcer is not running.');
4780
+ return;
4781
+ }
4782
+ s.config = { ...s.config, ...cfg };
4783
+ s.logger.log('Config updated:', cfg);
4784
+ }
4785
+
4786
+ /**
4787
+ * Computed Style Enforcer
4788
+ * Enforces computed CSS styles with viewport unit verification.
4789
+ */
4790
+ // ── Factory ───────────────────────────────────────────────────────────────────
4791
+ function createEnforcer() {
4792
+ const s = {
4793
+ logger: createLogger({ enabled: false, prefix: 'ComputedStyleEnforcer' }),
4794
+ doc: null,
4795
+ win: null,
4796
+ config: null,
4797
+ elementsWithViewportUnits: new Set(),
4798
+ originalValues: new WeakMap(),
4799
+ running: false,
4800
+ };
4801
+ return {
4802
+ start: (d, w, cfg, options) => start$3(s, d, w, cfg, options),
4803
+ stop: () => stop$3(s),
4804
+ reset: () => reset(s),
4805
+ trackElement: (element, propertyOriginalValues) => trackElement(s, element, propertyOriginalValues),
4806
+ processElement: (element, options) => processElement(s, element, options),
4807
+ processAll: (options) => processAll(s, options),
4808
+ updateConfig: (cfg) => updateConfig$2(s, cfg),
4809
+ getStateInfo: () => ({
4810
+ isRunning: s.running,
4811
+ trackedElementsCount: s.elementsWithViewportUnits.size,
4812
+ hasConfig: !!s.config,
4813
+ }),
4814
+ isRunning: () => s.running,
4815
+ };
4816
+ }
4817
+
4818
+ const registry$1 = [];
4819
+ /**
4820
+ * Register a global fix.
4821
+ * Fixes are run in registration order.
4822
+ */
4823
+ function register$1(fix) {
4824
+ registry$1.push(fix);
4825
+ }
4826
+ /**
4827
+ * Returns all fixes that are active for the given context.
4828
+ * A fix is active if it has no `shouldApply`, or `shouldApply` returns true.
4829
+ */
4830
+ function getActiveFixes(ctx) {
4831
+ return registry$1.filter((fix) => {
4832
+ try {
4833
+ return !fix.shouldApply || fix.shouldApply(ctx);
4834
+ }
4835
+ catch {
4836
+ return false;
4837
+ }
4838
+ });
4839
+ }
4423
4840
 
4424
4841
  /**
4425
4842
  * Core viewport unit replacement logic.
4426
4843
  * Converts vh/vw/svh/dvh/% to pixel values across all CSS in the iframe.
4427
4844
  */
4428
- const logger$4 = createLogger({ enabled: false, prefix: 'ViewportUnitReplacer' });
4845
+ const logger$1 = createLogger({ enabled: false, prefix: 'ViewportUnitReplacer' });
4429
4846
  // ─── Constants ────────────────────────────────────────────────────────────────
4430
4847
  const HEIGHT_RELATED_PROPERTIES = ['height', 'min-height', 'max-height', 'top', 'bottom'];
4431
4848
  // ─── Per-run tracking state (reset on each process() call) ───────────────────
4432
4849
  let elementsWithViewportUnits = new Set();
4433
4850
  let originalValues = new WeakMap();
4434
4851
  // ─── Regex ────────────────────────────────────────────────────────────────────
4435
- /** Fresh instance every call — avoids shared lastIndex state with the g flag. */
4852
+ /**
4853
+ * Stateless test-only regex (no `g` flag) — safe to share across calls.
4854
+ * Used exclusively for `.test()` checks before doing a full replacement.
4855
+ */
4856
+ const VIEWPORT_RE_TEST = /([-.?\d]+)(vh|svh|lvh|dvh|vw|svw|lvw|dvw)/i;
4857
+ /** Fresh `g`-flagged instance for String.replace() callbacks. */
4436
4858
  function createRegex() {
4437
- return /([-.\\d]+)(vh|svh|lvh|dvh|vw|svw|lvw|dvw)/gi;
4859
+ return /([-.?\d]+)(vh|svh|lvh|dvh|vw|svw|lvw|dvw)/gi;
4438
4860
  }
4439
4861
  // ─── Unit conversion ─────────────────────────────────────────────────────────
4440
4862
  function px(value) {
4441
4863
  return `${value.toFixed(2)}px`;
4442
4864
  }
4443
- function getUnitMap(ctx) {
4865
+ function buildUnitMap(ctx) {
4444
4866
  return {
4445
4867
  vh: ctx.targetHeight,
4446
4868
  svh: ctx.targetHeight,
@@ -4452,15 +4874,15 @@ function getUnitMap(ctx) {
4452
4874
  dvw: ctx.targetWidth,
4453
4875
  };
4454
4876
  }
4455
- function toPx(value, unit, ctx) {
4877
+ function toPx(value, unit, unitMap, targetHeight) {
4456
4878
  const u = unit.toLowerCase();
4457
4879
  if (u === '%')
4458
- return (value / 100) * ctx.targetHeight;
4459
- return (value / 100) * (getUnitMap(ctx)[u] ?? 0);
4880
+ return (value / 100) * targetHeight;
4881
+ return (value / 100) * (unitMap[u] ?? 0);
4460
4882
  }
4461
- function convert(value, unit, ctx) {
4883
+ function convert(value, unit, unitMap, targetHeight) {
4462
4884
  const num = parseFloat(value);
4463
- return isNaN(num) ? value : px(toPx(num, unit, ctx));
4885
+ return isNaN(num) ? value : px(toPx(num, unit, unitMap, targetHeight));
4464
4886
  }
4465
4887
  function isHeightRelated(prop) {
4466
4888
  return HEIGHT_RELATED_PROPERTIES.includes(prop);
@@ -4475,11 +4897,13 @@ function extractProperty(cssText, matchOffset) {
4475
4897
  return m ? m[1].toLowerCase() : '';
4476
4898
  }
4477
4899
  function replaceInText(cssText, ctx) {
4900
+ const unitMap = buildUnitMap(ctx);
4901
+ const { targetHeight } = ctx;
4478
4902
  return cssText.replace(createRegex(), (match, value, unit, offset) => {
4479
4903
  if (unit === '%') {
4480
- return isHeightRelated(extractProperty(cssText, offset)) ? convert(value, unit, ctx) : match;
4904
+ return isHeightRelated(extractProperty(cssText, offset)) ? convert(value, unit, unitMap, targetHeight) : match;
4481
4905
  }
4482
- return convert(value, unit, ctx);
4906
+ return convert(value, unit, unitMap, targetHeight);
4483
4907
  });
4484
4908
  }
4485
4909
  // ─── Element tracking ─────────────────────────────────────────────────────────
@@ -4496,11 +4920,11 @@ function trackSelector(selector, propOriginals, ctx) {
4496
4920
  if (!originals.has(k))
4497
4921
  originals.set(k, v);
4498
4922
  });
4499
- trackElement(el, propOriginals);
4923
+ ctx.enforcer?.trackElement(el, propOriginals);
4500
4924
  });
4501
4925
  }
4502
4926
  catch {
4503
- logger$4.warn('Invalid selector, skipping:', selector);
4927
+ logger$1.warn('Invalid selector, skipping:', selector);
4504
4928
  }
4505
4929
  }
4506
4930
  // ─── CSS processing ───────────────────────────────────────────────────────────
@@ -4508,25 +4932,25 @@ function processInlineStyles(ctx) {
4508
4932
  let count = 0;
4509
4933
  ctx.doc.querySelectorAll('[style]').forEach((el) => {
4510
4934
  const style = el.getAttribute('style');
4511
- if (style && createRegex().test(style)) {
4935
+ if (style && VIEWPORT_RE_TEST.test(style)) {
4512
4936
  elementsWithViewportUnits.add(el);
4513
4937
  el.setAttribute('style', replaceInText(style, ctx));
4514
4938
  count++;
4515
4939
  }
4516
4940
  });
4517
- logger$4.log(`Replaced ${count} inline style elements`);
4941
+ logger$1.log(`Replaced ${count} inline style elements`);
4518
4942
  return count;
4519
4943
  }
4520
4944
  function processStyleTags(ctx) {
4521
4945
  let count = 0;
4522
4946
  ctx.doc.querySelectorAll('style').forEach((tag) => {
4523
4947
  const css = tag.textContent || '';
4524
- if (createRegex().test(css)) {
4948
+ if (VIEWPORT_RE_TEST.test(css)) {
4525
4949
  tag.textContent = replaceInText(css, ctx);
4526
4950
  count++;
4527
4951
  }
4528
4952
  });
4529
- logger$4.log(`Replaced ${count} <style> tags`);
4953
+ logger$1.log(`Replaced ${count} <style> tags`);
4530
4954
  return count;
4531
4955
  }
4532
4956
  function processRule(rule, ctx) {
@@ -4539,7 +4963,7 @@ function processRule(rule, ctx) {
4539
4963
  for (let i = 0; i < style.length; i++) {
4540
4964
  const prop = style[i];
4541
4965
  const value = style.getPropertyValue(prop);
4542
- if (value && createRegex().test(value)) {
4966
+ if (value && VIEWPORT_RE_TEST.test(value)) {
4543
4967
  hasVp = true;
4544
4968
  propOriginals.set(prop, value);
4545
4969
  style.setProperty(prop, replaceInText(value, ctx), style.getPropertyPriority(prop));
@@ -4550,8 +4974,11 @@ function processRule(rule, ctx) {
4550
4974
  trackSelector(cssRule.selectorText, propOriginals, ctx);
4551
4975
  }
4552
4976
  if ('cssRules' in rule) {
4553
- for (const r of Array.from(rule.cssRules || [])) {
4554
- count += processRule(r, ctx);
4977
+ const nested = rule.cssRules;
4978
+ if (nested) {
4979
+ for (let i = 0; i < nested.length; i++) {
4980
+ count += processRule(nested[i], ctx);
4981
+ }
4555
4982
  }
4556
4983
  }
4557
4984
  return count;
@@ -4559,34 +4986,38 @@ function processRule(rule, ctx) {
4559
4986
  /** Processes only inline <style> sheets. Linked sheets are handled by processLinkedStylesheets. */
4560
4987
  function processStylesheets(ctx) {
4561
4988
  let total = 0;
4562
- Array.from(ctx.doc.styleSheets).forEach((sheet) => {
4989
+ const sheets = ctx.doc.styleSheets;
4990
+ for (let i = 0; i < sheets.length; i++) {
4991
+ const sheet = sheets[i];
4563
4992
  if (sheet.href)
4564
- return; // deferred to processLinkedStylesheets
4993
+ continue; // deferred to processLinkedStylesheets
4565
4994
  try {
4566
- for (const rule of Array.from(sheet.cssRules || [])) {
4567
- total += processRule(rule, ctx);
4995
+ const rules = sheet.cssRules;
4996
+ for (let j = 0; j < rules.length; j++) {
4997
+ total += processRule(rules[j], ctx);
4568
4998
  }
4569
4999
  }
4570
5000
  catch (e) {
4571
- logger$4.warn('Cannot read stylesheet (CORS?):', e.message);
5001
+ logger$1.warn('Cannot read stylesheet (CORS?):', e.message);
4572
5002
  }
4573
- });
4574
- logger$4.log(`Replaced ${total} rules in inline stylesheets`);
5003
+ }
5004
+ logger$1.log(`Replaced ${total} rules in inline stylesheets`);
4575
5005
  return total;
4576
5006
  }
4577
5007
  async function processLinkedStylesheets(ctx) {
4578
5008
  const links = ctx.doc.querySelectorAll('link[rel="stylesheet"]');
4579
5009
  let count = 0;
4580
- for (const link of Array.from(links)) {
5010
+ for (let i = 0; i < links.length; i++) {
5011
+ const link = links[i];
4581
5012
  // Skip cross-origin — already in browser CSSOM, handled via processStylesheets
4582
5013
  if (link.href && !link.href.startsWith(ctx.win.location.origin)) {
4583
- logger$4.log('Skipping cross-origin CSS:', link.href);
5014
+ logger$1.log('Skipping cross-origin CSS:', link.href);
4584
5015
  continue;
4585
5016
  }
4586
5017
  try {
4587
5018
  const res = await fetch(link.href);
4588
5019
  let css = await res.text();
4589
- if (createRegex().test(css)) {
5020
+ if (VIEWPORT_RE_TEST.test(css)) {
4590
5021
  css = replaceInText(css, ctx);
4591
5022
  const style = ctx.doc.createElement('style');
4592
5023
  style.textContent = css;
@@ -4597,15 +5028,15 @@ async function processLinkedStylesheets(ctx) {
4597
5028
  }
4598
5029
  }
4599
5030
  catch (e) {
4600
- logger$4.warn('Cannot load CSS:', link.href, e);
5031
+ logger$1.warn('Cannot load CSS:', link.href, e);
4601
5032
  }
4602
5033
  }
4603
- logger$4.log(`Replaced ${count} linked CSS files`);
5034
+ logger$1.log(`Replaced ${count} linked CSS files`);
4604
5035
  return count;
4605
5036
  }
4606
5037
  // ─── Public entry point ───────────────────────────────────────────────────────
4607
5038
  async function process$1(ctx) {
4608
- logger$4.configure({ enabled: !!ctx.debug });
5039
+ logger$1.configure({ enabled: !!ctx.debug });
4609
5040
  // Reset tracking state from any previous run
4610
5041
  elementsWithViewportUnits = new Set();
4611
5042
  originalValues = new WeakMap();
@@ -4616,8 +5047,8 @@ async function process$1(ctx) {
4616
5047
  // Wait for browser to apply the replaced styles
4617
5048
  await new Promise((resolve) => requestAnimationFrame(resolve));
4618
5049
  // Enforce final computed styles to inline with !important
4619
- const count = processAll({ debug: !!ctx.debug });
4620
- logger$4.log(`Enforced ${count} computed styles for ${elementsWithViewportUnits.size} tracked elements`);
5050
+ const count = ctx.enforcer?.processAll({ debug: !!ctx.debug }) ?? 0;
5051
+ logger$1.log(`Enforced ${count} computed styles for ${elementsWithViewportUnits.size} tracked elements`);
4621
5052
  }
4622
5053
 
4623
5054
  /**
@@ -4713,6 +5144,9 @@ function getMaxWByDeviceType(deviceType) {
4713
5144
  // ─── Main fix ─────────────────────────────────────────────────────────────────
4714
5145
  function afterProcess$1({ doc, targetWidth, deviceType }) {
4715
5146
  doc.querySelectorAll(SLIDER_ITEM_SELECTOR).forEach((item) => {
5147
+ // When !gp-min-w-full is set, the item fills 100% width via Tailwind — skip manual width override
5148
+ if (item.classList.contains('!gp-min-w-full'))
5149
+ return;
4716
5150
  const originalWidth = parseFloat(item.style.minWidth) || parseFloat(item.style.maxWidth) || 0;
4717
5151
  if (!originalWidth)
4718
5152
  return;
@@ -4813,23 +5247,6 @@ register$1({
4813
5247
  afterProcess,
4814
5248
  });
4815
5249
 
4816
- const logger$3 = createLogger({ enabled: false, prefix: 'ViewportReplacer' });
4817
- let dimensionsListener = null;
4818
- function attach(debug) {
4819
- logger$3.configure({ enabled: !!debug });
4820
- dimensionsListener = (e) => {
4821
- const ev = e;
4822
- logger$3.log('Dimensions applied:', ev.detail);
4823
- };
4824
- window.addEventListener('iframe-dimensions-applied', dimensionsListener);
4825
- }
4826
- function detach() {
4827
- if (dimensionsListener) {
4828
- window.removeEventListener('iframe-dimensions-applied', dimensionsListener);
4829
- dimensionsListener = null;
4830
- }
4831
- }
4832
-
4833
5250
  /**
4834
5251
  * Default iframe dimension calculation.
4835
5252
  * Used as fallback when no fix overrides getDimensions().
@@ -4853,41 +5270,58 @@ function getFinalWidth(doc) {
4853
5270
  * Phase 3: afterProcess (shop → global)
4854
5271
  * Phase 4: getDimensions (shop → global → default)
4855
5272
  */
4856
- const logger$2 = createLogger({ enabled: false, prefix: 'ViewportReplacer' });
5273
+ const logger = createLogger({ enabled: false, prefix: 'ViewportReplacer' });
4857
5274
  function configure(debug) {
4858
- logger$2.configure({ enabled: debug });
5275
+ logger.configure({ enabled: debug });
4859
5276
  }
4860
5277
  async function run$1(ctx, activeGlobal, shopFix) {
4861
5278
  // ── Phase 1: beforeProcess ────────────────────────────────────────────────
5279
+ const t1 = perf.mark('phase1.beforeProcess');
4862
5280
  for (const fix of activeGlobal) {
4863
5281
  if (fix.beforeProcess) {
4864
- logger$2.log(`[beforeProcess] ${fix.name}`);
5282
+ logger.log(`[beforeProcess] ${fix.name}`);
5283
+ const t = perf.mark(`phase1.${fix.name}.beforeProcess`);
4865
5284
  await fix.beforeProcess(ctx);
5285
+ perf.measure(`phase1.${fix.name}.beforeProcess`, t);
4866
5286
  }
4867
5287
  }
4868
5288
  if (shopFix?.beforeProcess) {
4869
- logger$2.log('[beforeProcess] shop');
5289
+ logger.log('[beforeProcess] shop');
5290
+ const t = perf.mark('phase1.shop.beforeProcess');
4870
5291
  await shopFix.beforeProcess(ctx);
5292
+ perf.measure('phase1.shop.beforeProcess', t);
4871
5293
  }
5294
+ perf.measure('phase1.beforeProcess', t1);
4872
5295
  // ── Phase 2: process ──────────────────────────────────────────────────────
5296
+ const t2 = perf.mark('phase2.process');
4873
5297
  for (const fix of activeGlobal) {
4874
5298
  if (fix.process) {
4875
- logger$2.log(`[process] ${fix.name}`);
5299
+ logger.log(`[process] ${fix.name}`);
5300
+ const t = perf.mark(`phase2.${fix.name}.process`);
4876
5301
  await fix.process(ctx);
5302
+ perf.measure(`phase2.${fix.name}.process`, t);
4877
5303
  }
4878
5304
  }
5305
+ perf.measure('phase2.process', t2);
4879
5306
  // ── Phase 3: afterProcess ─────────────────────────────────────────────────
5307
+ const t3 = perf.mark('phase3.afterProcess');
4880
5308
  if (shopFix?.afterProcess) {
4881
- logger$2.log('[afterProcess] shop');
5309
+ logger.log('[afterProcess] shop');
5310
+ const t = perf.mark('phase3.shop.afterProcess');
4882
5311
  await shopFix.afterProcess(ctx);
5312
+ perf.measure('phase3.shop.afterProcess', t);
4883
5313
  }
4884
5314
  for (const fix of activeGlobal) {
4885
5315
  if (fix.afterProcess) {
4886
- logger$2.log(`[afterProcess] ${fix.name}`);
5316
+ logger.log(`[afterProcess] ${fix.name}`);
5317
+ const t = perf.mark(`phase3.${fix.name}.afterProcess`);
4887
5318
  await fix.afterProcess(ctx);
5319
+ perf.measure(`phase3.${fix.name}.afterProcess`, t);
4888
5320
  }
4889
5321
  }
5322
+ perf.measure('phase3.afterProcess', t3);
4890
5323
  // ── Phase 4: getDimensions ────────────────────────────────────────────────
5324
+ const t4 = perf.mark('phase4.getDimensions');
4891
5325
  return new Promise((resolve) => {
4892
5326
  requestAnimationFrame(() => {
4893
5327
  let dimensions = null;
@@ -4895,14 +5329,14 @@ async function run$1(ctx, activeGlobal, shopFix) {
4895
5329
  if (shopFix?.getDimensions) {
4896
5330
  dimensions = shopFix.getDimensions(ctx);
4897
5331
  if (dimensions)
4898
- logger$2.log('Dimensions from shop fix:', dimensions);
5332
+ logger.log('Dimensions from shop fix:', dimensions);
4899
5333
  }
4900
5334
  if (!dimensions) {
4901
5335
  for (const fix of activeGlobal) {
4902
5336
  if (fix.getDimensions) {
4903
5337
  dimensions = fix.getDimensions(ctx);
4904
5338
  if (dimensions) {
4905
- logger$2.log(`Dimensions from global fix [${fix.name}]:`, dimensions);
5339
+ logger.log(`Dimensions from global fix [${fix.name}]:`, dimensions);
4906
5340
  break;
4907
5341
  }
4908
5342
  }
@@ -4911,7 +5345,8 @@ async function run$1(ctx, activeGlobal, shopFix) {
4911
5345
  if (!dimensions) {
4912
5346
  dimensions = { height: getFinalHeight(ctx.doc, ctx.win), width: getFinalWidth(ctx.doc) };
4913
5347
  }
4914
- logger$2.log('Final dimensions:', dimensions);
5348
+ logger.log('Final dimensions:', dimensions);
5349
+ perf.measure('phase4.getDimensions', t4);
4915
5350
  resolve(dimensions);
4916
5351
  });
4917
5352
  });
@@ -4988,232 +5423,286 @@ register(`566240210141053597`, {
4988
5423
  afterProcess: restoreAndFixLayout,
4989
5424
  });
4990
5425
 
4991
- const logger$1 = createLogger({ enabled: false, prefix: 'ViewportReplacer' });
4992
- // ─── State ────────────────────────────────────────────────────────────────────
4993
- let doc = null;
4994
- let win = null;
4995
- let config$1 = null;
4996
- let shopFix = null;
4997
- let running$2 = false;
4998
- // ─── Public API ───────────────────────────────────────────────────────────────
4999
- function start$3(iframe, cfg) {
5000
- if (running$2) {
5001
- logger$1.warn('Already running. Call stop() first.');
5426
+ // ── Module-level functions ────────────────────────────────────────────────────
5427
+ function start$2(s, iframe, cfg) {
5428
+ if (s.running) {
5429
+ s.logger.warn('Already running. Call stop() first.');
5002
5430
  return;
5003
5431
  }
5004
5432
  if (!iframe.contentDocument || !iframe.contentWindow) {
5005
5433
  throw new Error('Iframe document or window not accessible');
5006
5434
  }
5007
- doc = iframe.contentDocument;
5008
- win = iframe.contentWindow;
5009
- config$1 = cfg;
5010
- running$2 = true;
5011
- shopFix = cfg.shopId != null ? get(cfg.shopId) : null;
5012
- if (shopFix) {
5013
- logger$1.log(`Shop fix loaded for "${cfg.shopId}":`, shopFix.description ?? '(no description)');
5014
- }
5015
- logger$1.configure({ enabled: !!cfg.debug });
5435
+ s.doc = iframe.contentDocument;
5436
+ s.win = iframe.contentWindow;
5437
+ s.config = cfg;
5438
+ s.running = true;
5439
+ s.shopFix = cfg.shopId != null ? get(cfg.shopId) : null;
5440
+ if (s.shopFix)
5441
+ s.logger.log(`Shop fix loaded for "${cfg.shopId}":`, s.shopFix.description ?? '(no description)');
5442
+ s.logger.configure({ enabled: !!cfg.debug });
5016
5443
  configure(!!cfg.debug);
5017
- start$4(doc, win, cfg, { debug: !!cfg.debug });
5018
- attach(cfg.debug);
5019
- logger$1.log('Started');
5444
+ s.enforcer.start(s.doc, s.win, cfg, { debug: !!cfg.debug });
5445
+ s.listeners.attach(cfg.debug);
5446
+ s.logger.log('Started');
5020
5447
  }
5021
- function stop$3() {
5022
- if (!running$2)
5448
+ function stop$2(s) {
5449
+ if (!s.running)
5023
5450
  return;
5024
- stop$4();
5025
- detach();
5026
- doc = null;
5027
- win = null;
5028
- config$1 = null;
5029
- shopFix = null;
5030
- running$2 = false;
5031
- logger$1.log('Stopped');
5032
- }
5033
- async function run() {
5034
- if (!running$2 || !doc || !win || !config$1) {
5035
- logger$1.warn('Not running. Call start() first.');
5451
+ s.enforcer.stop();
5452
+ s.listeners.detach();
5453
+ s.doc = null;
5454
+ s.win = null;
5455
+ s.config = null;
5456
+ s.shopFix = null;
5457
+ s.running = false;
5458
+ s.logger.log('Stopped');
5459
+ }
5460
+ async function run(s) {
5461
+ if (!s.running || !s.doc || !s.win || !s.config) {
5462
+ s.logger.warn('Not running. Call start() first.');
5036
5463
  return { height: 1000, width: 1000 };
5037
5464
  }
5038
5465
  const ctx = {
5039
- doc,
5040
- win,
5041
- deviceType: config$1.deviceType,
5042
- targetWidth: config$1.targetWidth,
5043
- targetHeight: config$1.targetHeight,
5044
- debug: config$1.debug,
5466
+ doc: s.doc,
5467
+ win: s.win,
5468
+ deviceType: s.config.deviceType,
5469
+ targetWidth: s.config.targetWidth,
5470
+ targetHeight: s.config.targetHeight,
5471
+ debug: s.config.debug,
5472
+ enforcer: s.enforcer,
5045
5473
  };
5046
5474
  const activeGlobal = getActiveFixes(ctx);
5047
- if (activeGlobal.length > 0) {
5048
- logger$1.log(`Active global fixes: ${activeGlobal.map((f) => f.name).join(', ')}`);
5049
- }
5475
+ if (activeGlobal.length > 0)
5476
+ s.logger.log(`Active global fixes: ${activeGlobal.map((f) => f.name).join(', ')}`);
5477
+ const tRun = perf.mark('viewport.run');
5050
5478
  try {
5051
- return await run$1(ctx, activeGlobal, shopFix);
5479
+ const result = await run$1(ctx, activeGlobal, s.shopFix);
5480
+ perf.measure('viewport.run', tRun);
5481
+ return result;
5052
5482
  }
5053
5483
  catch (err) {
5054
- logger$1.error('Critical error:', err);
5055
- return { height: doc.body?.scrollHeight || 1000, width: doc.body?.scrollWidth || 1000 };
5484
+ perf.measure('viewport.run', tRun);
5485
+ s.logger.error('Critical error:', err);
5486
+ return { height: s.doc.body?.scrollHeight || 1000, width: s.doc.body?.scrollWidth || 1000 };
5056
5487
  }
5057
5488
  }
5058
-
5059
- const logger = createLogger({
5060
- enabled: false,
5061
- prefix: 'IframeFixer',
5062
- });
5063
- // ============================================================================
5064
- // State
5065
- // ============================================================================
5066
- let iframe = null;
5067
- let config = null;
5068
- let running$1 = false;
5069
- let loadListener = null;
5070
- // ============================================================================
5071
- // Core API Functions
5072
- // ============================================================================
5073
- function start$2(cfg) {
5074
- if (running$1) {
5075
- logger.warn('Fixer is already running. Call stop() first.');
5489
+ function updateConfig$1(s, cfg) {
5490
+ if (!s.running || !s.config) {
5491
+ s.logger.warn('Not running. Call start() first.');
5076
5492
  return;
5077
5493
  }
5078
- iframe = cfg.iframe;
5079
- config = cfg;
5080
- running$1 = true;
5081
- logger.configure({ enabled: !!cfg.debug });
5082
- logger.log('Iframe fixer started');
5083
- initialize();
5494
+ s.config = { ...s.config, ...cfg };
5495
+ if (cfg.shopId !== undefined)
5496
+ s.shopFix = cfg.shopId != null ? get(cfg.shopId) : null;
5497
+ s.enforcer.updateConfig(cfg);
5498
+ s.logger.log('Config updated');
5499
+ }
5500
+ // ── Factory ───────────────────────────────────────────────────────────────────
5501
+ function createViewportProcessor() {
5502
+ const s = {
5503
+ logger: createLogger({ enabled: false, prefix: 'ViewportReplacer' }),
5504
+ enforcer: createEnforcer(),
5505
+ listeners: createViewportListeners(),
5506
+ doc: null,
5507
+ win: null,
5508
+ config: null,
5509
+ shopFix: null,
5510
+ running: false,
5511
+ };
5512
+ return {
5513
+ start: (iframe, cfg) => start$2(s, iframe, cfg),
5514
+ stop: () => stop$2(s),
5515
+ run: () => run(s),
5516
+ updateConfig: (cfg) => updateConfig$1(s, cfg),
5517
+ isRunning: () => s.running,
5518
+ };
5084
5519
  }
5085
- function stop$2() {
5086
- if (!running$1) {
5520
+
5521
+ // ── Module-level functions ────────────────────────────────────────────────────
5522
+ function dispatchDimensionsEvent(dimensions) {
5523
+ window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', { detail: dimensions }));
5524
+ }
5525
+ async function process(s) {
5526
+ if (!s.iframe || !s.config)
5087
5527
  return;
5088
- }
5089
- // Stop viewport replacer
5090
- stop$3();
5091
- stop$5();
5092
- // Remove load listener
5093
- if (iframe && loadListener) {
5094
- iframe.removeEventListener('load', loadListener);
5095
- loadListener = null;
5096
- }
5097
- iframe = null;
5098
- config = null;
5099
- running$1 = false;
5100
- logger.log('Iframe fixer stopped');
5101
- }
5102
- async function initialize() {
5103
- if (!iframe || !config) {
5104
- logger.error('iframe not found');
5105
- config?.onError?.(new Error('iframe not found'));
5528
+ if (!s.iframe.contentDocument || !s.iframe.contentWindow) {
5529
+ s.logger.error('Cannot access iframe document');
5530
+ s.config.onError?.(new Error('Cannot access iframe document'));
5106
5531
  return;
5107
5532
  }
5108
- // Wait for iframe to load completely
5109
- if (iframe.contentDocument?.readyState === 'complete') {
5110
- await process();
5533
+ const sessionId = `render-${Date.now()}`;
5534
+ perf.startSession(sessionId);
5535
+ const t0 = perf.mark('orchestrator.process');
5536
+ try {
5537
+ s.logger.log('Processing viewport units...');
5538
+ s.viewportReplacer.start(s.iframe, s.config);
5539
+ s.navigationBlocker.start(s.iframe, { debug: s.config.debug });
5540
+ const result = await s.viewportReplacer.run();
5541
+ perf.measure('orchestrator.process', t0);
5542
+ perf.endSession();
5543
+ s.logger.log('Process completed:', result);
5544
+ s.config.onSuccess?.(result);
5545
+ dispatchDimensionsEvent(result);
5111
5546
  }
5112
- else {
5113
- loadListener = handleIframeLoad;
5114
- iframe.addEventListener('load', loadListener);
5547
+ catch (error) {
5548
+ perf.measure('orchestrator.process', t0);
5549
+ perf.endSession();
5550
+ s.logger.error('Failed to process:', error);
5551
+ s.config.onError?.(error);
5115
5552
  }
5116
5553
  }
5117
- async function process() {
5118
- if (!iframe || !config)
5119
- return;
5120
- if (!iframe.contentDocument || !iframe.contentWindow) {
5121
- logger.error('Cannot access iframe document');
5122
- config.onError?.(new Error('Cannot access iframe document'));
5554
+ async function initialize(s) {
5555
+ if (!s.iframe || !s.config) {
5556
+ s.logger.error('iframe not found');
5557
+ s.config?.onError?.(new Error('iframe not found'));
5123
5558
  return;
5124
5559
  }
5125
- try {
5126
- logger.log('Processing viewport units...');
5127
- start$3(iframe, config);
5128
- start$5(iframe, { debug: config.debug });
5129
- const result = await run();
5130
- logger.log('Process completed:', result);
5131
- config.onSuccess?.(result);
5132
- dispatchDimensionsEvent(result);
5133
- // Optionally setup height observer
5134
- // setupHeightObserver();
5560
+ if (s.iframe.contentDocument?.readyState === 'complete') {
5561
+ await process(s);
5135
5562
  }
5136
- catch (error) {
5137
- logger.error('Failed to process:', error);
5138
- config.onError?.(error);
5563
+ else {
5564
+ s.loadListener = () => process(s);
5565
+ s.iframe.addEventListener('load', s.loadListener);
5139
5566
  }
5140
5567
  }
5141
- // ============================================================================
5142
- // Helper Functions
5143
- // ============================================================================
5144
- function dispatchDimensionsEvent(dimensions) {
5145
- window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', {
5146
- detail: dimensions,
5147
- }));
5568
+ function start$1(s, cfg) {
5569
+ if (s.running) {
5570
+ s.logger.warn('Fixer is already running. Call stop() first.');
5571
+ return;
5572
+ }
5573
+ s.iframe = cfg.iframe;
5574
+ s.config = cfg;
5575
+ s.running = true;
5576
+ s.logger.configure({ enabled: !!cfg.debug });
5577
+ s.logger.log('Iframe fixer started');
5578
+ initialize(s);
5148
5579
  }
5149
- function handleIframeLoad() {
5150
- process();
5580
+ function stop$1(s) {
5581
+ if (!s.running)
5582
+ return;
5583
+ s.viewportReplacer.stop();
5584
+ s.heightObserver.stop();
5585
+ s.navigationBlocker.stop();
5586
+ if (s.iframe && s.loadListener) {
5587
+ s.iframe.removeEventListener('load', s.loadListener);
5588
+ s.loadListener = null;
5589
+ }
5590
+ s.iframe = null;
5591
+ s.config = null;
5592
+ s.running = false;
5593
+ s.logger.log('Iframe fixer stopped');
5594
+ }
5595
+ async function recalculate$1(s) {
5596
+ if (!s.running) {
5597
+ s.logger.warn('Fixer is not running.');
5598
+ return;
5599
+ }
5600
+ s.logger.log('Recalculating...');
5601
+ await process(s);
5151
5602
  }
5152
- /**
5153
- * Enable navigation blocking
5154
- */
5155
- function enableNavigationBlocking$2() {
5156
- if (!running$1) {
5157
- logger.warn('Fixer is not running. Call start() first.');
5603
+ function updateConfig(s, cfg) {
5604
+ if (!s.running || !s.config) {
5605
+ s.logger.warn('Fixer is not running.');
5158
5606
  return;
5159
5607
  }
5160
- enable();
5608
+ s.config = { ...s.config, ...cfg };
5609
+ s.viewportReplacer.updateConfig(cfg);
5610
+ s.logger.log('Config updated');
5611
+ }
5612
+ // ── Factory ───────────────────────────────────────────────────────────────────
5613
+ function createOrchestrator() {
5614
+ const s = {
5615
+ logger: createLogger({ enabled: false, prefix: 'IframeFixer' }),
5616
+ viewportReplacer: createViewportProcessor(),
5617
+ navigationBlocker: createNavigationBlocker(),
5618
+ heightObserver: createHeightObserver(),
5619
+ iframe: null,
5620
+ config: null,
5621
+ running: false,
5622
+ loadListener: null,
5623
+ };
5624
+ return {
5625
+ start: (cfg) => start$1(s, cfg),
5626
+ stop: () => stop$1(s),
5627
+ recalculate: () => recalculate$1(s),
5628
+ updateConfig: (cfg) => updateConfig(s, cfg),
5629
+ enableNavigationBlocking: () => s.navigationBlocker.enable(),
5630
+ enableNavigationBlockingMessage: () => s.navigationBlocker.enableMessage(),
5631
+ disableNavigationBlocking: () => s.navigationBlocker.disable(),
5632
+ disableNavigationBlockingMessage: () => s.navigationBlocker.disableMessage(),
5633
+ isRunning: () => s.running,
5634
+ getStateInfo: () => ({
5635
+ isRunning: s.running,
5636
+ hasIframe: !!s.iframe,
5637
+ hasConfig: !!s.config,
5638
+ hasNavigationBlocker: s.navigationBlocker.isRunning(),
5639
+ hasHeightObserver: s.heightObserver.isRunning(),
5640
+ viewportReplacerRunning: s.viewportReplacer.isRunning(),
5641
+ }),
5642
+ };
5161
5643
  }
5162
5644
 
5163
5645
  /**
5164
- * Iframe Helper Starter Module
5165
- * @module start
5646
+ * Iframe Helper factory entry point.
5647
+ *
5648
+ * Each call to `createIframeHelper()` returns a fully isolated instance
5649
+ * with its own processor state. Use one instance per iframe.
5166
5650
  */
5167
- // ============================================================================
5168
- // State
5169
- // ============================================================================
5170
- let running = false;
5171
- // ============================================================================
5172
- // Public API
5173
- // ============================================================================
5174
- function start$1(config) {
5175
- if (running) {
5176
- console.warn('[IframeHelperStarter] Already running. Call stop() first.');
5651
+ // ── Module-level functions ────────────────────────────────────────────────────
5652
+ function start(s, config) {
5653
+ if (s.running) {
5654
+ console.warn('[IframeHelper] Already running. Call stop() first.');
5177
5655
  return;
5178
5656
  }
5179
- start$2(config);
5180
- running = true;
5657
+ s.fixer.start(config);
5658
+ s.running = true;
5181
5659
  }
5182
- function stop$1() {
5183
- if (!running) {
5660
+ function stop(s) {
5661
+ if (!s.running)
5184
5662
  return;
5185
- }
5186
- stop$2();
5187
- running = false;
5663
+ s.fixer.stop();
5664
+ s.running = false;
5188
5665
  }
5189
- function enableNavigationBlocking$1() {
5190
- if (!running) {
5191
- console.warn('[IframeHelperStarter] Not running. Call start() first.');
5666
+ async function recalculate(s) {
5667
+ if (!s.running) {
5668
+ console.warn('[IframeHelper] Not running. Call start() first.');
5192
5669
  return;
5193
5670
  }
5194
- enableNavigationBlocking$2();
5195
- }
5196
-
5197
- function start(config) {
5198
- start$1(config);
5671
+ await s.fixer.recalculate();
5199
5672
  }
5200
- function stop() {
5201
- stop$1();
5673
+ function enableNavigationBlocking(s) {
5674
+ if (!s.running) {
5675
+ console.warn('[IframeHelper] Not running. Call start() first.');
5676
+ return;
5677
+ }
5678
+ s.fixer.enableNavigationBlocking();
5202
5679
  }
5203
- function enableNavigationBlocking() {
5204
- enableNavigationBlocking$1();
5680
+ // ── Factory ───────────────────────────────────────────────────────────────────
5681
+ function createIframeHelper() {
5682
+ const s = {
5683
+ fixer: createOrchestrator(),
5684
+ running: false,
5685
+ };
5686
+ return {
5687
+ start: (config) => start(s, config),
5688
+ stop: () => stop(s),
5689
+ recalculate: () => recalculate(s),
5690
+ enableNavigationBlocking: () => enableNavigationBlocking(s),
5691
+ isRunning: () => s.running,
5692
+ };
5205
5693
  }
5206
5694
 
5695
+ const iframeHelper = createIframeHelper();
5207
5696
  function useVizLiveRender() {
5208
5697
  const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
5209
5698
  const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
5210
5699
  const wrapperWidth = useHeatmapVizRectContext((s) => s.wrapperWidth);
5211
- const setIsRenderViz = useHeatmapVizContext((s) => s.setIsRenderViz);
5212
- const htmlContent = useHeatmapLiveStore((s) => s.htmlContent);
5213
- const targetUrl = useHeatmapLiveStore((s) => s.targetUrl);
5700
+ const setIsRenderedViz = useHeatmapVizContext((s) => s.setIsRenderedViz);
5701
+ const htmlContent = useHeatmapLiveContext((s) => s.htmlContent);
5702
+ const targetUrl = useHeatmapLiveContext((s) => s.targetUrl);
5214
5703
  const deviceType = useHeatmapSettingContext((s) => s.deviceType);
5215
- const renderMode = useHeatmapLiveStore((s) => s.renderMode);
5216
- const storefrontPassword = useHeatmapLiveStore((s) => s.storefrontPassword);
5704
+ const renderMode = useHeatmapLiveContext((s) => s.renderMode);
5705
+ const storefrontPassword = useHeatmapLiveContext((s) => s.storefrontPassword);
5217
5706
  useHeatmapWidthByDevice();
5218
5707
  const { iframeRef, isReady } = useVizLiveIframeMsg();
5219
5708
  // Handle iframe rendering based on mode
@@ -5254,10 +5743,10 @@ function useVizLiveRender() {
5254
5743
  const hasContent = (renderMode === 'portal' && targetUrl) || (renderMode === 'inline' && htmlContent);
5255
5744
  if (!iframe || !hasContent)
5256
5745
  return;
5257
- setIsRenderViz(true);
5258
- initIframeHelper$1(iframe, deviceType, { width: wrapperWidth, height: wrapperHeight }, (height) => {
5746
+ setIsRenderedViz(true);
5747
+ startIframe$1(iframe, deviceType, { width: wrapperWidth, height: wrapperHeight }, (height) => {
5259
5748
  height && setIframeHeight(height);
5260
- setIsRenderViz(true);
5749
+ setIsRenderedViz(true);
5261
5750
  });
5262
5751
  return () => { };
5263
5752
  }, [
@@ -5269,7 +5758,7 @@ function useVizLiveRender() {
5269
5758
  targetUrl,
5270
5759
  htmlContent,
5271
5760
  iframeRef,
5272
- setIsRenderViz,
5761
+ setIsRenderedViz,
5273
5762
  setIframeHeight,
5274
5763
  ]);
5275
5764
  return {
@@ -5288,9 +5777,9 @@ function buildPortalUrl(targetUrl, storefrontPassword) {
5288
5777
  const portalServiceUrl = getPortalServiceUrl();
5289
5778
  return `${portalServiceUrl}/?${params.toString()}`;
5290
5779
  }
5291
- function initIframeHelper$1(iframe, deviceType = EDeviceType.Desktop, rect, onSuccess) {
5292
- stop();
5293
- start({
5780
+ function startIframe$1(iframe, deviceType = EDeviceType.Desktop, rect, onSuccess) {
5781
+ iframeHelper.stop();
5782
+ iframeHelper.start({
5294
5783
  deviceType: deviceType,
5295
5784
  targetWidth: rect.width,
5296
5785
  targetHeight: rect.height,
@@ -5301,7 +5790,7 @@ function initIframeHelper$1(iframe, deviceType = EDeviceType.Desktop, rect, onSu
5301
5790
  },
5302
5791
  });
5303
5792
  // fixer.recalculate();
5304
- enableNavigationBlocking();
5793
+ iframeHelper.enableNavigationBlocking();
5305
5794
  }
5306
5795
 
5307
5796
  const CANVAS_ID = 'clarity-heatmap-canvas';
@@ -5551,36 +6040,67 @@ class GXVisualizer extends Visualizer {
5551
6040
  };
5552
6041
  }
5553
6042
 
6043
+ // ── Performance timing ────────────────────────────────────────────────────────
6044
+ function mark(label) {
6045
+ const t = performance.now();
6046
+ console.log(`[Render] ⏱ ${label}`);
6047
+ return t;
6048
+ }
6049
+ function measure(label, startMs) {
6050
+ const ms = (performance.now() - startMs).toFixed(1);
6051
+ console.log(`[Render] ✅ ${label} — ${ms}ms`);
6052
+ }
6053
+ // ── Hook ──────────────────────────────────────────────────────────────────────
5554
6054
  const useHeatmapRender = () => {
5555
6055
  const viewId = useViewIdContext();
5556
6056
  const data = useHeatmapDataContext((s) => s.data);
5557
6057
  const vizRef = useHeatmapVizRectContext((s) => s.vizRef);
5558
6058
  const setVizRef = useHeatmapVizRectContext((s) => s.setVizRef);
5559
6059
  const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
5560
- const setIsRenderViz = useHeatmapVizContext((s) => s.setIsRenderViz);
6060
+ const setIsRenderedViz = useHeatmapVizContext((s) => s.setIsRenderedViz);
5561
6061
  const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
5562
6062
  const contentWidth = useHeatmapWidthByDevice();
5563
6063
  const deviceType = useHeatmapSettingContext((s) => s.deviceType);
5564
6064
  const iframeRef = useRef(null);
6065
+ const helperRef = useRef(null);
6066
+ const abortRef = useRef(null);
5565
6067
  const renderHeatmap = useCallback(async (payloads) => {
5566
6068
  if (contentWidth === 0 || wrapperHeight === 0)
5567
6069
  return;
5568
6070
  if (!payloads || payloads.length === 0)
5569
6071
  return;
6072
+ // Cancel any in-flight render before starting a new one
6073
+ abortRef.current?.abort();
6074
+ const abort = new AbortController();
6075
+ abortRef.current = abort;
6076
+ const t0 = mark('renderHeatmap start');
5570
6077
  const visualizer = vizRef ?? new GXVisualizer();
5571
6078
  if (!vizRef)
5572
6079
  setVizRef(visualizer);
5573
- setIsRenderViz(false);
6080
+ setIsRenderedViz(false);
5574
6081
  const iframe = iframeRef.current;
5575
6082
  if (!iframe?.contentWindow)
5576
6083
  return;
6084
+ const tHtml = mark('visualizer.html start');
5577
6085
  await visualizer.html(payloads, iframe.contentWindow, viewId);
5578
- const size = { width: contentWidth, height: wrapperHeight };
5579
- initIframeHelper(iframe, deviceType, size, (height) => {
5580
- height && setIframeHeight(height);
5581
- setIsRenderViz(true);
6086
+ measure('visualizer.html', tHtml);
6087
+ // Bail out if data changed or component unmounted while we were awaiting
6088
+ if (abort.signal.aborted)
6089
+ return;
6090
+ startIframe({
6091
+ helperRef,
6092
+ iframe,
6093
+ deviceType,
6094
+ size: { width: contentWidth, height: wrapperHeight },
6095
+ t0,
6096
+ onSuccess: (height) => {
6097
+ if (abort.signal.aborted)
6098
+ return;
6099
+ if (height)
6100
+ setIframeHeight(height);
6101
+ setIsRenderedViz(true);
6102
+ },
5582
6103
  });
5583
- // setIsRenderViz(true);
5584
6104
  }, [wrapperHeight, contentWidth, deviceType]);
5585
6105
  useEffect(() => {
5586
6106
  if (!data || data.length === 0)
@@ -5591,33 +6111,46 @@ const useHeatmapRender = () => {
5591
6111
  setVizRef(null);
5592
6112
  };
5593
6113
  }, [data, renderHeatmap, setVizRef]);
6114
+ // Abort render + stop helper when the component unmounts
6115
+ useEffect(() => {
6116
+ return () => {
6117
+ abortRef.current?.abort();
6118
+ helperRef.current?.stop();
6119
+ helperRef.current = null;
6120
+ };
6121
+ }, []);
5594
6122
  return {
5595
6123
  iframeRef,
5596
6124
  };
5597
6125
  };
5598
- function initIframeHelper(iframe, deviceType = EDeviceType.Desktop, size, onSuccess) {
6126
+ // ── Helpers ───────────────────────────────────────────────────────────────────
6127
+ function startIframe({ helperRef, iframe, deviceType = EDeviceType.Desktop, size, t0, onSuccess }) {
5599
6128
  const docWidth = size.width ?? 0;
5600
6129
  const docHeight = size.height ?? 0;
5601
6130
  if (docHeight === 0)
5602
6131
  return;
5603
- stop();
5604
- start({
5605
- deviceType: deviceType,
6132
+ helperRef.current?.stop();
6133
+ const tHelper = mark('IframeHelper.start');
6134
+ const helper = createIframeHelper();
6135
+ helperRef.current = helper;
6136
+ helper.start({
6137
+ deviceType,
5606
6138
  targetWidth: docWidth,
5607
6139
  targetHeight: docHeight,
5608
- iframe: iframe,
6140
+ iframe,
5609
6141
  debug: true,
5610
6142
  onSuccess: (data) => {
6143
+ measure('IframeHelper processing', tHelper);
6144
+ measure('Total render', t0);
5611
6145
  iframe.style.height = `${data.height}px`;
5612
6146
  onSuccess(data.height);
5613
6147
  },
5614
6148
  });
5615
- // fixer.recalculate();
5616
6149
  }
5617
6150
 
5618
6151
  const useReplayRender = () => {
5619
6152
  const data = useHeatmapDataContext((s) => s.data);
5620
- const setIsRenderViz = useHeatmapVizContext((s) => s.setIsRenderViz);
6153
+ const setIsRenderedViz = useHeatmapVizContext((s) => s.setIsRenderedViz);
5621
6154
  const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
5622
6155
  const visualizerRef = useRef(null);
5623
6156
  const iframeRef = useRef(null);
@@ -5637,7 +6170,7 @@ const useReplayRender = () => {
5637
6170
  version: envelope.version,
5638
6171
  onresize: (height) => {
5639
6172
  height && setIframeHeight(height);
5640
- setIsRenderViz(true);
6173
+ setIsRenderedViz(true);
5641
6174
  },
5642
6175
  mobile,
5643
6176
  vNext: true,
@@ -5793,7 +6326,7 @@ const useContentDimensions = ({ iframeRef }) => {
5793
6326
  const useObserveIframeHeight = (props) => {
5794
6327
  const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
5795
6328
  const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
5796
- const isRenderViz = useHeatmapVizContext((s) => s.isRenderViz);
6329
+ const isRenderedViz = useHeatmapVizContext((s) => s.isRenderedViz);
5797
6330
  const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
5798
6331
  const { iframeRef } = props;
5799
6332
  const resizeObserverRef = useRef(null);
@@ -5854,7 +6387,7 @@ const useObserveIframeHeight = (props) => {
5854
6387
  }, [updateIframeHeight]);
5855
6388
  useEffect(() => {
5856
6389
  const iframe = iframeRef.current;
5857
- if (!iframe || !iframeHeight || !isRenderViz)
6390
+ if (!iframe || !iframeHeight || !isRenderedViz)
5858
6391
  return;
5859
6392
  const setupObservers = () => {
5860
6393
  try {
@@ -5916,7 +6449,7 @@ const useObserveIframeHeight = (props) => {
5916
6449
  }
5917
6450
  iframe.removeEventListener('load', setupObservers);
5918
6451
  };
5919
- }, [iframeRef, iframeHeight, isRenderViz]);
6452
+ }, [iframeRef, iframeHeight, isRenderedViz]);
5920
6453
  return {};
5921
6454
  };
5922
6455
 
@@ -6215,10 +6748,10 @@ const useScrollmapZones = (options) => {
6215
6748
  const newZones = createZones(scrollmap);
6216
6749
  setZones(newZones);
6217
6750
  setIsReady(true);
6218
- logger$9.log(`[useScrollmap] Created ${newZones.length} zones in ${mode} mode`);
6751
+ logger$4.log(`[useScrollmap] Created ${newZones.length} zones in ${mode} mode`);
6219
6752
  }
6220
6753
  catch (error) {
6221
- logger$9.error('[useScrollmap] Error:', error);
6754
+ logger$4.error('[useScrollmap] Error:', error);
6222
6755
  setIsReady(false);
6223
6756
  }
6224
6757
  }, [enabled, scrollmap, mode, createZones]);
@@ -7048,11 +7581,11 @@ const AutoScrollHandler = ({ visualRef }) => {
7048
7581
  };
7049
7582
 
7050
7583
  const PortalAreaRenderer = ({ iframeRef, visualRef, shadowRoot, onAreaCreated, onAreaClick, }) => {
7051
- const isRenderViz = useHeatmapVizContext((s) => s.isRenderViz);
7584
+ const isRenderedViz = useHeatmapVizContext((s) => s.isRenderedViz);
7052
7585
  const iframeDocument = iframeRef.current?.contentDocument || undefined;
7053
7586
  const { shadowContainer, isReady } = useAreaRendererContainer(iframeDocument, shadowRoot);
7054
- useAreaRectSync({ iframeDocument, shadowRoot, enabled: isReady && isRenderViz });
7055
- useAreaPositionsUpdater({ iframeRef, visualRef, enabled: isReady && isRenderViz });
7587
+ useAreaRectSync({ iframeDocument, shadowRoot, enabled: isReady && isRenderedViz });
7588
+ useAreaPositionsUpdater({ iframeRef, visualRef, enabled: isReady && isRenderedViz });
7056
7589
  if (!shadowContainer || !isReady)
7057
7590
  return null;
7058
7591
  return (jsxs(Fragment$1, { children: [jsx(AutoScrollHandler, { visualRef: visualRef }), jsx(AreasPortal, { shadowContainer: shadowContainer, onAreaClick: onAreaClick }), jsx(AreaEditHighlightPortal, { shadowContainer: shadowContainer, iframeRef: iframeRef, customShadowRoot: shadowRoot, onAreaCreated: onAreaCreated })] }));
@@ -7061,7 +7594,7 @@ const PortalAreaRenderer = ({ iframeRef, visualRef, shadowRoot, onAreaCreated, o
7061
7594
  const VizAreaClick = ({ iframeRef, visualRef, shadowRoot, autoCreateTopN = 10, enableOverlapResolution = true, onAreaClick, }) => {
7062
7595
  const clickAreas = useHeatmapDataContext((s) => s.clickAreas);
7063
7596
  const resetView = useHeatmapAreaClickContext((s) => s.resetView);
7064
- const isRenderViz = useHeatmapVizContext((s) => s.isRenderViz);
7597
+ const isRenderedViz = useHeatmapVizContext((s) => s.isRenderedViz);
7065
7598
  useAreaTopAutoDetect({ autoCreateTopN, shadowRoot, disabled: !!clickAreas?.length });
7066
7599
  useAreaFilterVisible({ iframeRef, enableOverlapResolution });
7067
7600
  useAreaHydration({ shadowRoot });
@@ -7070,7 +7603,7 @@ const VizAreaClick = ({ iframeRef, visualRef, shadowRoot, autoCreateTopN = 10, e
7070
7603
  resetView();
7071
7604
  };
7072
7605
  }, []);
7073
- if (!iframeRef.current || !isRenderViz)
7606
+ if (!iframeRef.current || !isRenderedViz)
7074
7607
  return null;
7075
7608
  return (jsx(Fragment, { children: jsx(PortalAreaRenderer, { iframeRef: iframeRef, visualRef: visualRef, shadowRoot: shadowRoot, onAreaClick: onAreaClick }) }));
7076
7609
  };
@@ -7784,7 +8317,7 @@ const VizDomHeatmap = () => {
7784
8317
  const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
7785
8318
  const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
7786
8319
  const setVizRef = useHeatmapVizRectContext((s) => s.setVizRef);
7787
- const setIsRenderViz = useHeatmapVizContext((s) => s.setIsRenderViz);
8320
+ const setIsRenderedViz = useHeatmapVizContext((s) => s.setIsRenderedViz);
7788
8321
  const setSelectedElement = useHeatmapClickContext((s) => s.setSelectedElement);
7789
8322
  const setHoveredElement = useHeatmapHoverContext((s) => s.setHoveredElement);
7790
8323
  // const setSelectedArea = useHeatmapAreaClickContext((s) => s.setSelectedArea);
@@ -7794,7 +8327,7 @@ const VizDomHeatmap = () => {
7794
8327
  const cleanUp = () => {
7795
8328
  setVizRef(null);
7796
8329
  setIframeHeight(0);
7797
- setIsRenderViz(false);
8330
+ setIsRenderedViz(false);
7798
8331
  setSelectedElement(null);
7799
8332
  setHoveredElement(null);
7800
8333
  // setSelectedArea(null);
@@ -7826,7 +8359,7 @@ const VizLiveRenderer = () => {
7826
8359
  const VizLiveHeatmap = () => {
7827
8360
  const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
7828
8361
  const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
7829
- useHeatmapLiveStore((state) => state.reset);
8362
+ useHeatmapLiveContext((s) => s.reset);
7830
8363
  const CompVizLoading = useHeatmapControlStore((state) => state.controls.VizLoading);
7831
8364
  // TODO: Remove this after testing
7832
8365
  useEffect(() => {
@@ -7911,4 +8444,4 @@ const HeatmapLayout = ({ data, clickmap, clickAreas, scrollmap, attentionMap, co
7911
8444
  }
7912
8445
  };
7913
8446
 
7914
- export { BACKDROP_CONFIG, DEFAULT_SIDEBAR_WIDTH, DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO, EClickMode, EClickRankType, EClickType, EDeviceType, EHeatmapMode, EHeatmapType, ELM_CALLOUT_CONFIG, EScrollType, GraphView, HEATMAP_CONFIG, HEATMAP_IFRAME, HEATMAP_STYLE, HeatmapLayout, ViewIdContext, Z_INDEX$1 as Z_INDEX, compareViewPerformance, convertViewportToIframeCoords, createStorePerformanceTracker, createViewContextHook, decodeArrayClarity, decodeClarity, downloadPerformanceReport, getCompareViewId, getMetricsByViewId, getPerformanceReportJSON, getScrollGradientColor, performanceLogger, printPerformanceSummary, scrollToElementIfNeeded, sendPerformanceReport, serializeAreas, trackStoreAction, useAreaCreation, useAreaEditMode, useAreaFilterVisible, useAreaHydration, useAreaInteraction, useAreaPositionsUpdater, useAreaRectSync, useAreaRendererContainer, useAreaTopAutoDetect, useClickedElement, useDebounceCallback, useElementCalloutVisible, useHeatmapAreaClickContext, useHeatmapCanvas, useHeatmapClickContext, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapCopyView, useHeatmapDataContext, useHeatmapEffects, useHeatmapElementPosition, useHeatmapHoverContext, useHeatmapLiveStore, useHeatmapRenderByMode, useHeatmapScale, useHeatmapScroll, useHeatmapScrollContext, useHeatmapSettingContext, useHeatmapVizContext, useHeatmapVizRectContext, useHeatmapWidthByDevice, useHoveredElement, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewIdContext, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };
8447
+ export { BACKDROP_CONFIG, DEFAULT_SIDEBAR_WIDTH, DEFAULT_VIEW_ID, DEFAULT_ZOOM_RATIO, EClickMode, EClickRankType, EClickType, EDeviceType, EHeatmapDataSource, EHeatmapMode, EHeatmapType, ELM_CALLOUT_CONFIG, EScrollType, GraphView, HEATMAP_CONFIG, HEATMAP_IFRAME, HEATMAP_STYLE, HeatmapLayout, ViewIdContext, Z_INDEX$1 as Z_INDEX, compareViewPerformance, convertViewportToIframeCoords, createStorePerformanceTracker, createViewContextHook, decodeArrayClarity, decodeClarity, downloadPerformanceReport, getCompareViewId, getMetricsByViewId, getPerformanceReportJSON, getScrollGradientColor, performanceLogger, printPerformanceSummary, scrollToElementIfNeeded, sendPerformanceReport, serializeAreas, trackStoreAction, useAreaCreation, useAreaEditMode, useAreaFilterVisible, useAreaHydration, useAreaInteraction, useAreaPositionsUpdater, useAreaRectSync, useAreaRendererContainer, useAreaTopAutoDetect, useClickedElement, useDebounceCallback, useElementCalloutVisible, useHeatmapAreaClickContext, useHeatmapCanvas, useHeatmapClickContext, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapCopyView, useHeatmapDataContext, useHeatmapEffects, useHeatmapElementPosition, useHeatmapHoverContext, useHeatmapLiveContext, useHeatmapRenderByMode, useHeatmapScale, useHeatmapScroll, useHeatmapScrollContext, useHeatmapSettingContext, useHeatmapVizContext, useHeatmapVizRectContext, useHeatmapWidthByDevice, useHoveredElement, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewIdContext, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };