@gemx-dev/clarity-visualize 0.8.62 → 0.8.64

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.
@@ -67,105 +67,6 @@ function __generator(thisArg, body) {
67
67
  }
68
68
  }
69
69
 
70
- var _a$1;
71
- var DataHelper = /** @class */ (function () {
72
- function DataHelper(state) {
73
- var _this = this;
74
- this.regionMap = {};
75
- this.regions = {};
76
- this.metrics = {};
77
- this.lean = false;
78
- this.reset = function () {
79
- _this.metrics = {};
80
- _this.lean = false;
81
- _this.regions = {};
82
- _this.regionMap = {};
83
- };
84
- this.metric = function (event) {
85
- var _a;
86
- if (_this.state.options.metadata) {
87
- var metricMarkup = [];
88
- var regionMarkup = [];
89
- // Copy over metrics for future reference
90
- for (var m in event.data) {
91
- var eventType = typeof event.data[m];
92
- if (eventType === "number" || (event.event === 1 /* Data.Event.Dimension */ && m === 37 /* Data.Dimension.InteractionNextPaint */.toString())) {
93
- if (!(m in _this.metrics)) {
94
- _this.metrics[m] = 0;
95
- }
96
- var key = parseInt(m, 10);
97
- var value = eventType === "object" ? Number((_a = event.data[m]) === null || _a === void 0 ? void 0 : _a[0]) : event.data[m];
98
- if (m in DataHelper.METRIC_MAP && (DataHelper.METRIC_MAP[m].unit === "html-price" || DataHelper.METRIC_MAP[m].unit === "ld-price")) {
99
- _this.metrics[m] = value;
100
- }
101
- else {
102
- _this.metrics[m] += value;
103
- }
104
- _this.lean = key === 1 /* Data.Metric.Playback */ && value === 0 ? true : _this.lean;
105
- }
106
- }
107
- for (var entry in _this.metrics) {
108
- if (entry in DataHelper.METRIC_MAP) {
109
- var m = _this.metrics[entry];
110
- var map = DataHelper.METRIC_MAP[entry];
111
- var unit = "unit" in map ? map.unit : "" /* Data.Constant.Empty */;
112
- metricMarkup.push("<li><h2>".concat(_this.value(m, unit), "<span>").concat(_this.key(unit), "</span></h2>").concat(map.name, "</li>"));
113
- }
114
- }
115
- // Append region information to metadata
116
- for (var name_1 in _this.regions) {
117
- var r = _this.regions[name_1];
118
- var className = (r.visibility === 10 /* Layout.RegionVisibility.Visible */ ? "visible" : (r.interaction === 20 /* Layout.InteractionState.Clicked */ ? "clicked" : "" /* Data.Constant.Empty */));
119
- regionMarkup.push("<span class=\"".concat(className, "\">").concat(name_1, "</span>"));
120
- }
121
- _this.state.options.metadata.innerHTML = "<ul>".concat(metricMarkup.join("" /* Data.Constant.Empty */), "</ul><div>").concat(regionMarkup.join("" /* Data.Constant.Empty */), "</div>");
122
- }
123
- };
124
- this.key = function (unit) {
125
- switch (unit) {
126
- case "html-price":
127
- case "ld-price":
128
- case "cls":
129
- return "" /* Data.Constant.Empty */;
130
- default: return unit;
131
- }
132
- };
133
- this.value = function (num, unit) {
134
- switch (unit) {
135
- case "KB": return Math.round(num / 1024);
136
- case "s": return Math.round(num / 10) / 100;
137
- case "cls": return num / 1000;
138
- case "html-price": return num / 100;
139
- default: return num;
140
- }
141
- };
142
- this.state = state;
143
- }
144
- DataHelper.prototype.region = function (event) {
145
- var data = event.data;
146
- for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
147
- var r = data_1[_i];
148
- if (!(r.name in this.regions)) {
149
- this.regions[r.name] = { interaction: r.interaction, visibility: r.visibility };
150
- }
151
- this.regionMap[r.id] = r.name;
152
- }
153
- };
154
- DataHelper.METRIC_MAP = (_a$1 = {},
155
- _a$1[2 /* Data.Metric.TotalBytes */] = { name: "Total Bytes", unit: "KB" },
156
- _a$1[4 /* Data.Metric.TotalCost */] = { name: "Total Cost", unit: "ms" },
157
- _a$1[3 /* Data.Metric.LayoutCost */] = { name: "Layout Cost", unit: "ms" },
158
- _a$1[8 /* Data.Metric.LargestPaint */] = { name: "LCP", unit: "s" },
159
- _a$1[9 /* Data.Metric.CumulativeLayoutShift */] = { name: "CLS", unit: "cls" },
160
- _a$1[7 /* Data.Metric.LongTaskCount */] = { name: "Long Tasks" },
161
- _a$1[24 /* Data.Metric.CartTotal */] = { name: "Cart Total", unit: "html-price" },
162
- _a$1[13 /* Data.Metric.ProductPrice */] = { name: "Product Price", unit: "ld-price" },
163
- _a$1[6 /* Data.Metric.ThreadBlockedTime */] = { name: "Thread Blocked", unit: "ms" },
164
- _a$1[37 /* Data.Dimension.InteractionNextPaint */] = { name: "INP", unit: "ms" },
165
- _a$1);
166
- return DataHelper;
167
- }());
168
-
169
70
  // tslint:disable: no-bitwise
170
71
  function hash (input, precision) {
171
72
  if (precision === void 0) { precision = null; }
@@ -186,12 +87,27 @@ function hash (input, precision) {
186
87
  hash = Math.abs(hashOne + (hashTwo * 11579));
187
88
  return (precision ? hash % Math.pow(2, precision) : hash).toString(36);
188
89
  }
90
+
91
+ function __spreadArray(to, from, pack) {
92
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
93
+ if (ar || !(i in from)) {
94
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
95
+ ar[i] = from[i];
96
+ }
97
+ }
98
+ return to.concat(ar || Array.prototype.slice.call(from));
99
+ }
189
100
 
190
101
  var excludeClassNames = "load,active,fixed,visible,focus,show,collaps,animat" /* Constant.ExcludeClassNames */.split("," /* Constant.Comma */);
102
+ var extraExcludeClassNames = [];
191
103
  var selectorMap = {};
192
104
  function reset$8() {
193
105
  selectorMap = {};
194
106
  }
107
+ function config$2(classNames) {
108
+ extraExcludeClassNames = classNames || [];
109
+ console.log("[Selector] config called \u2014 extraExcludeClassNames set to: [".concat(extraExcludeClassNames.join(', '), "]"));
110
+ }
195
111
  function get$1(input, type) {
196
112
  var a = input.attributes;
197
113
  var prefix = input.prefix ? input.prefix[type] : null;
@@ -214,7 +130,13 @@ function get$1(input, type) {
214
130
  input.tag = input.tag.indexOf("svg:" /* Constant.SvgPrefix */) === 0 ? input.tag.substr("svg:" /* Constant.SvgPrefix */.length) : input.tag;
215
131
  var selector = "".concat(prefix).concat(input.tag).concat(suffix);
216
132
  var id = "id" /* Constant.Id */ in a && a["id" /* Constant.Id */].length > 0 ? a["id" /* Constant.Id */] : null;
217
- var classes = input.tag !== "BODY" /* Constant.BodyTag */ && "class" /* Constant.Class */ in a && a["class" /* Constant.Class */].length > 0 ? a["class" /* Constant.Class */].trim().split(/\s+/).filter(function (c) { return filter(c); }).join("." /* Constant.Period */) : null;
133
+ var rawClasses = "class" /* Constant.Class */ in a ? a["class" /* Constant.Class */].trim().split(/\s+/) : [];
134
+ var filteredClasses = rawClasses.filter(function (c) { return filter(c); });
135
+ var removedClasses = rawClasses.filter(function (c) { return !filter(c); });
136
+ if (removedClasses.length > 0) {
137
+ console.log("[Selector] tag=\"".concat(input.tag, "\" id=\"").concat(input.id, "\" \u2014 removed classes: [").concat(removedClasses.join(', '), "] | kept: [").concat(filteredClasses.join(', '), "] | extraExclude: [").concat(extraExcludeClassNames.join(', '), "]"));
138
+ }
139
+ var classes = input.tag !== "BODY" /* Constant.BodyTag */ && filteredClasses.length > 0 ? filteredClasses.join("." /* Constant.Period */) : null;
218
140
  if (classes && classes.length > 0) {
219
141
  if (type === 0 /* Selector.Alpha */) {
220
142
  // In Alpha mode, update selector to use class names, with relative positioning within the parent id container.
@@ -227,16 +149,19 @@ function get$1(input, type) {
227
149
  selectorMap[key].push(input.id);
228
150
  }
229
151
  selector = "".concat(key).concat("~" /* Constant.Tilde */).concat(selectorMap[key].indexOf(input.id));
152
+ console.log("\uD83D\uDE80 \uD83D\uDC25 ~ get ~ selector:", selector);
230
153
  }
231
154
  else {
232
155
  // In Beta mode, we continue to look at query selectors in context of the full page
233
156
  selector = "".concat(prefix).concat(input.tag, ".").concat(classes).concat(suffix);
157
+ console.log("\uD83D\uDE80 \uD83D\uDC25 ~ get ~ selector:", selector);
234
158
  }
235
159
  }
236
160
  // Update selector to use "id" field when available. There are two exceptions:
237
161
  // (1) if "id" appears to be an auto generated string token, e.g. guid or a random id containing digits
238
162
  // (2) if "id" appears inside a shadow DOM, in which case we continue to prefix up to shadow DOM to prevent conflicts
239
163
  selector = id && filter(id) ? "".concat(getDomPrefix(prefix)).concat("#" /* Constant.Hash */).concat(id) : selector;
164
+ console.log("\uD83D\uDE80 \uD83D\uDC25 ~ get ~ selector:", selector);
240
165
  return selector;
241
166
  }
242
167
  }
@@ -263,6 +188,8 @@ function filter(value) {
263
188
  if (!value) {
264
189
  return false;
265
190
  } // Do not process empty strings
191
+ var excludeClassNames = getExcludeClassNames();
192
+ console.log("\uD83D\uDE80 \uD83D\uDC25 ~ filter ~ excludeClassNames:", excludeClassNames);
266
193
  if (excludeClassNames.some(function (x) { return value.toLowerCase().indexOf(x) >= 0; })) {
267
194
  return false;
268
195
  }
@@ -274,9 +201,15 @@ function filter(value) {
274
201
  }
275
202
  return true;
276
203
  }
204
+ function getExcludeClassNames() {
205
+ console.log("\uD83D\uDE80 \uD83D\uDC25 ~ getExcludeClassNames ~ extraExcludeClassNames:", extraExcludeClassNames);
206
+ console.log("\uD83D\uDE80 \uD83D\uDC25 ~ getExcludeClassNames ~ excludeClassNames:", excludeClassNames);
207
+ return __spreadArray(__spreadArray([], excludeClassNames, true), extraExcludeClassNames, true);
208
+ }
277
209
 
278
210
  var selector = /*#__PURE__*/Object.freeze({
279
211
  __proto__: null,
212
+ config: config$2,
280
213
  get: get$1,
281
214
  reset: reset$8
282
215
  });
@@ -312,6 +245,105 @@ function lookup(hash) {
312
245
 
313
246
  var helper = { hash: hash, selector: selector, get: get$2, getNode: getNode, lookup: lookup };
314
247
 
248
+ var _a$1;
249
+ var DataHelper = /** @class */ (function () {
250
+ function DataHelper(state) {
251
+ var _this = this;
252
+ this.regionMap = {};
253
+ this.regions = {};
254
+ this.metrics = {};
255
+ this.lean = false;
256
+ this.reset = function () {
257
+ _this.metrics = {};
258
+ _this.lean = false;
259
+ _this.regions = {};
260
+ _this.regionMap = {};
261
+ };
262
+ this.metric = function (event) {
263
+ var _a;
264
+ if (_this.state.options.metadata) {
265
+ var metricMarkup = [];
266
+ var regionMarkup = [];
267
+ // Copy over metrics for future reference
268
+ for (var m in event.data) {
269
+ var eventType = typeof event.data[m];
270
+ if (eventType === "number" || (event.event === 1 /* Data.Event.Dimension */ && m === 37 /* Data.Dimension.InteractionNextPaint */.toString())) {
271
+ if (!(m in _this.metrics)) {
272
+ _this.metrics[m] = 0;
273
+ }
274
+ var key = parseInt(m, 10);
275
+ var value = eventType === "object" ? Number((_a = event.data[m]) === null || _a === void 0 ? void 0 : _a[0]) : event.data[m];
276
+ if (m in DataHelper.METRIC_MAP && (DataHelper.METRIC_MAP[m].unit === "html-price" || DataHelper.METRIC_MAP[m].unit === "ld-price")) {
277
+ _this.metrics[m] = value;
278
+ }
279
+ else {
280
+ _this.metrics[m] += value;
281
+ }
282
+ _this.lean = key === 1 /* Data.Metric.Playback */ && value === 0 ? true : _this.lean;
283
+ }
284
+ }
285
+ for (var entry in _this.metrics) {
286
+ if (entry in DataHelper.METRIC_MAP) {
287
+ var m = _this.metrics[entry];
288
+ var map = DataHelper.METRIC_MAP[entry];
289
+ var unit = "unit" in map ? map.unit : "" /* Data.Constant.Empty */;
290
+ metricMarkup.push("<li><h2>".concat(_this.value(m, unit), "<span>").concat(_this.key(unit), "</span></h2>").concat(map.name, "</li>"));
291
+ }
292
+ }
293
+ // Append region information to metadata
294
+ for (var name_1 in _this.regions) {
295
+ var r = _this.regions[name_1];
296
+ var className = (r.visibility === 10 /* Layout.RegionVisibility.Visible */ ? "visible" : (r.interaction === 20 /* Layout.InteractionState.Clicked */ ? "clicked" : "" /* Data.Constant.Empty */));
297
+ regionMarkup.push("<span class=\"".concat(className, "\">").concat(name_1, "</span>"));
298
+ }
299
+ _this.state.options.metadata.innerHTML = "<ul>".concat(metricMarkup.join("" /* Data.Constant.Empty */), "</ul><div>").concat(regionMarkup.join("" /* Data.Constant.Empty */), "</div>");
300
+ }
301
+ };
302
+ this.key = function (unit) {
303
+ switch (unit) {
304
+ case "html-price":
305
+ case "ld-price":
306
+ case "cls":
307
+ return "" /* Data.Constant.Empty */;
308
+ default: return unit;
309
+ }
310
+ };
311
+ this.value = function (num, unit) {
312
+ switch (unit) {
313
+ case "KB": return Math.round(num / 1024);
314
+ case "s": return Math.round(num / 10) / 100;
315
+ case "cls": return num / 1000;
316
+ case "html-price": return num / 100;
317
+ default: return num;
318
+ }
319
+ };
320
+ this.state = state;
321
+ }
322
+ DataHelper.prototype.region = function (event) {
323
+ var data = event.data;
324
+ for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
325
+ var r = data_1[_i];
326
+ if (!(r.name in this.regions)) {
327
+ this.regions[r.name] = { interaction: r.interaction, visibility: r.visibility };
328
+ }
329
+ this.regionMap[r.id] = r.name;
330
+ }
331
+ };
332
+ DataHelper.METRIC_MAP = (_a$1 = {},
333
+ _a$1[2 /* Data.Metric.TotalBytes */] = { name: "Total Bytes", unit: "KB" },
334
+ _a$1[4 /* Data.Metric.TotalCost */] = { name: "Total Cost", unit: "ms" },
335
+ _a$1[3 /* Data.Metric.LayoutCost */] = { name: "Layout Cost", unit: "ms" },
336
+ _a$1[8 /* Data.Metric.LargestPaint */] = { name: "LCP", unit: "s" },
337
+ _a$1[9 /* Data.Metric.CumulativeLayoutShift */] = { name: "CLS", unit: "cls" },
338
+ _a$1[7 /* Data.Metric.LongTaskCount */] = { name: "Long Tasks" },
339
+ _a$1[24 /* Data.Metric.CartTotal */] = { name: "Cart Total", unit: "html-price" },
340
+ _a$1[13 /* Data.Metric.ProductPrice */] = { name: "Product Price", unit: "ld-price" },
341
+ _a$1[6 /* Data.Metric.ThreadBlockedTime */] = { name: "Thread Blocked", unit: "ms" },
342
+ _a$1[37 /* Data.Dimension.InteractionNextPaint */] = { name: "INP", unit: "ms" },
343
+ _a$1);
344
+ return DataHelper;
345
+ }());
346
+
315
347
  var EnrichHelper = /** @class */ (function () {
316
348
  function EnrichHelper() {
317
349
  var _this = this;
@@ -356,6 +388,10 @@ var EnrichHelper = /** @class */ (function () {
356
388
  var selectorBeta = helper.selector.get(input, 1 /* Layout.Selector.Beta */);
357
389
  d.selectorBeta = selectorBeta.length > 0 ? selectorBeta : null;
358
390
  d.hashBeta = selectorBeta.length > 0 ? helper.hash(d.selectorBeta) : null;
391
+ var rawClass = (d.attributes || {})['class'] || '';
392
+ if (rawClass.includes('gp_ls-is-cached')) {
393
+ console.log("[Enrich] id=".concat(d.id, " tag=").concat(d.tag, " | class=\"").concat(rawClass, "\" | selectorBeta=\"").concat(selectorBeta, "\" | hashBeta=").concat(d.hashBeta));
394
+ }
359
395
  /* Track state for future reference */
360
396
  node.alpha = selectorAlpha;
361
397
  node.beta = selectorBeta;
@@ -882,12 +918,28 @@ var HeatmapHelper = /** @class */ (function () {
882
918
  var points = {};
883
919
  var localMax = 0;
884
920
  var height = _this.state.window && _this.state.window.document ? _this.state.window.document.documentElement.clientHeight : 0;
921
+ // DEBUG LOG
922
+ console.group("[Heatmap] transform \u2014 total elements: ".concat(_this.data.length, ", height: ").concat(height, ", max: ").concat(_this.max));
885
923
  for (var _i = 0, _a = _this.data; _i < _a.length; _i++) {
886
924
  var element = _a[_i];
887
925
  var el = _this.layout.get(element.hash);
926
+ if (!el) {
927
+ console.warn("[Heatmap] SKIP hash=\"".concat(element.hash, "\" \u2014 element not found in DOM"));
928
+ continue;
929
+ }
930
+ if (typeof el.getBoundingClientRect !== "function") {
931
+ console.warn("[Heatmap] SKIP hash=\"".concat(element.hash, "\" \u2014 getBoundingClientRect not available"), el);
932
+ continue;
933
+ }
934
+ var r = el.getBoundingClientRect();
935
+ var isDebugHash = element.hash === "270qs0pw7";
936
+ var v = _this.visible(el, r, height, isDebugHash ? element.hash : undefined);
937
+ if (!v && _this.max !== null) {
938
+ console.warn("[Heatmap] SKIP hash=\"".concat(element.hash, "\" \u2014 not visible on re-render"), { rect: { top: r.top, left: r.left, width: r.width, height: r.height }, maxIsSet: _this.max !== null });
939
+ continue;
940
+ }
888
941
  if (el && typeof el.getBoundingClientRect === "function") {
889
- var r = el.getBoundingClientRect();
890
- var v = _this.visible(el, r, height);
942
+ console.log("[Heatmap] PROCESS hash=\"".concat(element.hash, "\" visible=").concat(v, " points=").concat(element.points), { rect: { top: r.top, left: r.left, width: r.width, height: r.height } });
891
943
  // Process clicks for only visible elements
892
944
  if (_this.max === null || v) {
893
945
  for (var i = 0; i < element.points; i++) {
@@ -896,6 +948,7 @@ var HeatmapHelper = /** @class */ (function () {
896
948
  var k = "".concat(x).concat("X" /* Constant.Separator */).concat(y).concat("X" /* Constant.Separator */).concat(v ? 1 : 0);
897
949
  points[k] = k in points ? points[k] + element.clicks[i] : element.clicks[i];
898
950
  localMax = Math.max(points[k], localMax);
951
+ console.log(" point[".concat(i, "] x=").concat(x, " y=").concat(y, " visible=").concat(v, " clicks=").concat(element.clicks[i]));
899
952
  }
900
953
  }
901
954
  }
@@ -903,6 +956,7 @@ var HeatmapHelper = /** @class */ (function () {
903
956
  // Set the max value from the firs t
904
957
  _this.max = _this.max ? _this.max : localMax;
905
958
  // Once all points are accounted for, convert everything into absolute (x, y)
959
+ var skippedInvisible = 0;
906
960
  for (var _b = 0, _c = Object.keys(points); _b < _c.length; _b++) {
907
961
  var coordinates = _c[_b];
908
962
  var parts = coordinates.split("X" /* Constant.Separator */);
@@ -910,16 +964,36 @@ var HeatmapHelper = /** @class */ (function () {
910
964
  if (parts[2] === "1") {
911
965
  output.push({ x: parseInt(parts[0], 10), y: parseInt(parts[1], 10), a: alpha });
912
966
  }
967
+ else {
968
+ skippedInvisible++;
969
+ }
913
970
  }
971
+ console.log("[Heatmap] Result \u2014 drawn: ".concat(output.length, ", skipped invisible: ").concat(skippedInvisible, ", max: ").concat(_this.max));
972
+ console.groupEnd();
914
973
  return output;
915
974
  };
916
- this.visible = function (el, r, height) {
975
+ this.visible = function (el, r, height, debugHash) {
917
976
  var doc = _this.state.window.document;
918
977
  var visibility = r.height > height ? true : false;
978
+ if (debugHash) {
979
+ console.group("[Heatmap visible] hash=".concat(debugHash, " tag=").concat(el === null || el === void 0 ? void 0 : el.tagName));
980
+ console.log(" rect: top=".concat(r.top.toFixed(1), " bottom=").concat(r.bottom.toFixed(1), " left=").concat(r.left.toFixed(1), " width=").concat(r.width.toFixed(1), " height=").concat(r.height.toFixed(1)));
981
+ console.log(" viewport height=".concat(height, " | r.height > height? ").concat(r.height > height));
982
+ }
919
983
  if (visibility === false && r.width > 0 && r.height > 0) {
984
+ var checkX = r.left + (r.width / 2);
985
+ var checkY = r.top + (r.height / 2);
986
+ if (debugHash) {
987
+ console.log(" elementsFromPoint(".concat(checkX.toFixed(1), ", ").concat(checkY.toFixed(1), ")"));
988
+ }
920
989
  while (!visibility && doc) {
921
990
  var shadowElement = null;
922
- var elements = doc.elementsFromPoint(r.left + (r.width / 2), r.top + (r.height / 2));
991
+ var elements = doc.elementsFromPoint(checkX, checkY);
992
+ if (debugHash) {
993
+ var topElements = elements.slice(0, 5).map(function (e) { return "".concat(e.tagName).concat(e.id ? '#' + e.id : '').concat(e.className ? '.' + String(e.className).trim().split(/\s+/).slice(0, 2).join('.') : ''); });
994
+ console.log(" top elements at center: [".concat(topElements.join(', '), "]"));
995
+ console.log(" is el in top elements? ".concat(elements.includes(el), " | el matches first non-canvas? ").concat(elements.find(function (e) { return !(e.tagName === "CANVAS" /* Constant.Canvas */ || (e.id && e.id.indexOf("clarity-" /* Constant.ClarityPrefix */) === 0)); }) === el));
996
+ }
923
997
  for (var _i = 0, elements_1 = elements; _i < elements_1.length; _i++) {
924
998
  var e = elements_1[_i];
925
999
  // Ignore if top element ends up being the canvas element we added for heatmap visualization
@@ -933,7 +1007,18 @@ var HeatmapHelper = /** @class */ (function () {
933
1007
  doc = shadowElement;
934
1008
  }
935
1009
  }
936
- return visibility && r.bottom >= 0 && r.top <= height;
1010
+ else if (debugHash) {
1011
+ if (r.width === 0 || r.height === 0) {
1012
+ console.log(" SKIP elementsFromPoint \u2014 zero dimension: width=".concat(r.width, " height=").concat(r.height));
1013
+ }
1014
+ }
1015
+ var result = visibility && r.bottom >= 0 && r.top <= height;
1016
+ if (debugHash) {
1017
+ console.log(" visibility=".concat(visibility, " | r.bottom(").concat(r.bottom.toFixed(1), ") >= 0? ").concat(r.bottom >= 0, " | r.top(").concat(r.top.toFixed(1), ") <= height(").concat(height, ")? ").concat(r.top <= height));
1018
+ console.log(" => final result: ".concat(result));
1019
+ console.groupEnd();
1020
+ }
1021
+ return result;
937
1022
  };
938
1023
  this.waitForDialogs = function () { return __awaiter(_this, void 0, void 0, function () {
939
1024
  var isPortalCanvas;
@@ -1682,6 +1767,9 @@ var LayoutHelper = /** @class */ (function () {
1682
1767
  // In case of selector collision, prefer the first inserted node
1683
1768
  _this.hashMapAlpha[data.hashAlpha] = _this.get(data.hashAlpha) || parent;
1684
1769
  _this.hashMapBeta[data.hashBeta] = _this.get(data.hashBeta) || parent;
1770
+ if (data.hashBeta === '2e36mrf7c' || data.hashBeta === '19lq10ve5') {
1771
+ console.log("[Layout] addToHashMap hashBeta=".concat(data.hashBeta, " | hashAlpha=").concat(data.hashAlpha, " | tag=").concat(parent === null || parent === void 0 ? void 0 : parent.tagName));
1772
+ }
1685
1773
  };
1686
1774
  this.resize = function (el, width, height) {
1687
1775
  if (el && el.nodeType === 1 /* NodeType.ELEMENT_NODE */ && width && height) {
@@ -2310,6 +2398,11 @@ var Visualizer = /** @class */ (function () {
2310
2398
  this._state = null;
2311
2399
  this.renderTime = 0;
2312
2400
  this.hashFoundTime = -1;
2401
+ this._excludeClassNames = [];
2402
+ this.configure = function (opts) {
2403
+ _this._excludeClassNames = opts.excludeClassNames || [];
2404
+ helper.selector.configure(_this._excludeClassNames);
2405
+ };
2313
2406
  this.dom = function (event) { return __awaiter(_this, void 0, void 0, function () {
2314
2407
  return __generator(this, function (_a) {
2315
2408
  switch (_a.label) {
@@ -2346,25 +2439,33 @@ var Visualizer = /** @class */ (function () {
2346
2439
  return false;
2347
2440
  }
2348
2441
  };
2349
- this.html = function (decoded, target, portalCanvasId, hash, useproxy, logerror, shortCircuitStrategy) {
2442
+ this.html = function (decoded, target, portalCanvasId, hash, useproxy, logerror, shortCircuitStrategy, signal) {
2350
2443
  if (hash === void 0) { hash = null; }
2351
2444
  if (shortCircuitStrategy === void 0) { shortCircuitStrategy = 0 /* ShortCircuitStrategy.None */; }
2352
2445
  return __awaiter(_this, void 0, void 0, function () {
2353
- var merged, entry, _a, domEvent, e_1;
2446
+ var merged, CHUNK_BUDGET_MS, chunkStart, entry, _a, domEvent, e_1;
2354
2447
  return __generator(this, function (_b) {
2355
2448
  switch (_b.label) {
2356
2449
  case 0:
2357
- if (!(decoded && decoded.length > 0 && target)) return [3 /*break*/, 11];
2450
+ if (!(decoded && decoded.length > 0 && target)) return [3 /*break*/, 13];
2358
2451
  _b.label = 1;
2359
2452
  case 1:
2360
- _b.trys.push([1, 10, , 11]);
2453
+ _b.trys.push([1, 12, , 13]);
2361
2454
  merged = this.merge(decoded);
2455
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
2456
+ return [2 /*return*/, this];
2362
2457
  return [4 /*yield*/, this.setup(target, { version: decoded[0].envelope.version, dom: merged.dom, useproxy: useproxy, portalCanvasId: portalCanvasId })];
2363
2458
  case 2:
2364
2459
  _b.sent();
2460
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
2461
+ return [2 /*return*/, this];
2462
+ CHUNK_BUDGET_MS = 8;
2463
+ chunkStart = performance.now();
2365
2464
  _b.label = 3;
2366
2465
  case 3:
2367
- if (!(merged.events.length > 0)) return [3 /*break*/, 9];
2466
+ if (!(merged.events.length > 0)) return [3 /*break*/, 11];
2467
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
2468
+ return [2 /*return*/, this];
2368
2469
  entry = merged.events.shift();
2369
2470
  _a = entry.event;
2370
2471
  switch (_a) {
@@ -2390,15 +2491,22 @@ var Visualizer = /** @class */ (function () {
2390
2491
  case 7:
2391
2492
  _b.sent();
2392
2493
  return [3 /*break*/, 8];
2393
- case 8: return [3 /*break*/, 3];
2394
- case 9: return [3 /*break*/, 11];
2395
- case 10:
2494
+ case 8:
2495
+ if (!(performance.now() - chunkStart >= CHUNK_BUDGET_MS)) return [3 /*break*/, 10];
2496
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
2497
+ case 9:
2498
+ _b.sent();
2499
+ chunkStart = performance.now();
2500
+ _b.label = 10;
2501
+ case 10: return [3 /*break*/, 3];
2502
+ case 11: return [3 /*break*/, 13];
2503
+ case 12:
2396
2504
  e_1 = _b.sent();
2397
2505
  if (logerror) {
2398
2506
  logerror(e_1);
2399
2507
  }
2400
- return [3 /*break*/, 11];
2401
- case 11: return [2 /*return*/, this];
2508
+ return [3 /*break*/, 13];
2509
+ case 13: return [2 /*return*/, this];
2402
2510
  }
2403
2511
  });
2404
2512
  });
@@ -2430,6 +2538,7 @@ var Visualizer = /** @class */ (function () {
2430
2538
  decoded = decoded.sort(_this.sortPayloads);
2431
2539
  // Re-initialize enrich class if someone ends up calling merge function directly
2432
2540
  _this.enrich = _this.enrich || new EnrichHelper();
2541
+ helper.selector.configure(_this._excludeClassNames);
2433
2542
  _this.enrich.reset();
2434
2543
  // Walk through payloads and generate merged payload from an array of decoded payloads
2435
2544
  for (var _i = 0, decoded_1 = decoded; _i < decoded_1.length; _i++) {
@@ -2468,6 +2577,10 @@ var Visualizer = /** @class */ (function () {
2468
2577
  switch (_a.label) {
2469
2578
  case 0:
2470
2579
  this.reset();
2580
+ if (options.excludeClassNames) {
2581
+ this._excludeClassNames = options.excludeClassNames;
2582
+ helper.selector.configure(options.excludeClassNames);
2583
+ }
2471
2584
  // Infer options
2472
2585
  options.pointer = "pointer" in options ? options.pointer : true;
2473
2586
  options.canvas = "canvas" in options ? options.canvas : true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gemx-dev/clarity-visualize",
3
- "version": "0.8.62",
3
+ "version": "0.8.64",
4
4
  "description": "Clarity visualize",
5
5
  "author": "Microsoft Corp.",
6
6
  "license": "MIT",
@@ -9,7 +9,7 @@
9
9
  "unpkg": "build/clarity.visualize.min.js",
10
10
  "types": "types/index.d.ts",
11
11
  "dependencies": {
12
- "@gemx-dev/clarity-decode": "^0.8.62"
12
+ "@gemx-dev/clarity-decode": "^0.8.64"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@rollup/plugin-commonjs": "^24.0.0",
package/src/heatmap.ts CHANGED
@@ -276,11 +276,32 @@ export class HeatmapHelper {
276
276
  let points: { [key: string]: number } = {};
277
277
  let localMax = 0;
278
278
  let height = this.state.window && this.state.window.document ? this.state.window.document.documentElement.clientHeight : 0;
279
+
280
+ // DEBUG LOG
281
+ console.group(`[Heatmap] transform — total elements: ${this.data.length}, height: ${height}, max: ${this.max}`);
282
+
279
283
  for (let element of this.data) {
280
284
  let el = this.layout.get(element.hash) as HTMLElement;
285
+ if (!el) {
286
+ console.warn(`[Heatmap] SKIP hash="${element.hash}" — element not found in DOM`);
287
+ continue;
288
+ }
289
+ if (typeof el.getBoundingClientRect !== "function") {
290
+ console.warn(`[Heatmap] SKIP hash="${element.hash}" — getBoundingClientRect not available`, el);
291
+ continue;
292
+ }
293
+
294
+ let r = el.getBoundingClientRect();
295
+ const isDebugHash = element.hash === "270qs0pw7";
296
+ let v = this.visible(el, r, height, isDebugHash ? element.hash : undefined);
297
+
298
+ if (!v && this.max !== null) {
299
+ console.warn(`[Heatmap] SKIP hash="${element.hash}" — not visible on re-render`, { rect: { top: r.top, left: r.left, width: r.width, height: r.height }, maxIsSet: this.max !== null });
300
+ continue;
301
+ }
302
+
281
303
  if (el && typeof el.getBoundingClientRect === "function") {
282
- let r = el.getBoundingClientRect();
283
- let v = this.visible(el, r, height);
304
+ console.log(`[Heatmap] PROCESS hash="${element.hash}" visible=${v} points=${element.points}`, { rect: { top: r.top, left: r.left, width: r.width, height: r.height } });
284
305
  // Process clicks for only visible elements
285
306
  if (this.max === null || v) {
286
307
  for(let i = 0; i < element.points; i++) {
@@ -289,6 +310,7 @@ export class HeatmapHelper {
289
310
  let k = `${x}${Constant.Separator}${y}${Constant.Separator}${v ? 1 : 0}`;
290
311
  points[k] = k in points ? points[k] + element.clicks[i] : element.clicks[i];
291
312
  localMax = Math.max(points[k], localMax);
313
+ console.log(` point[${i}] x=${x} y=${y} visible=${v} clicks=${element.clicks[i]}`);
292
314
  }
293
315
  }
294
316
  }
@@ -298,23 +320,49 @@ export class HeatmapHelper {
298
320
  this.max = this.max ? this.max : localMax;
299
321
 
300
322
  // Once all points are accounted for, convert everything into absolute (x, y)
323
+ let skippedInvisible = 0;
301
324
  for (let coordinates of Object.keys(points)) {
302
325
  let parts = coordinates.split(Constant.Separator);
303
326
  let alpha = Math.min((points[coordinates] / this.max) + Setting.AlphaBoost, 1);
304
- if (parts[2] === "1") { output.push({ x: parseInt(parts[0], 10), y: parseInt(parts[1], 10), a: alpha }); }
327
+ if (parts[2] === "1") {
328
+ output.push({ x: parseInt(parts[0], 10), y: parseInt(parts[1], 10), a: alpha });
329
+ } else {
330
+ skippedInvisible++;
331
+ }
305
332
  }
306
333
 
334
+ console.log(`[Heatmap] Result — drawn: ${output.length}, skipped invisible: ${skippedInvisible}, max: ${this.max}`);
335
+ console.groupEnd();
336
+
307
337
  return output;
308
338
  }
309
339
 
310
- private visible = (el: HTMLElement, r: DOMRect, height: number): boolean => {
340
+ private visible = (el: HTMLElement, r: DOMRect, height: number, debugHash?: string): boolean => {
311
341
  let doc: Document | ShadowRoot = this.state.window.document;
312
342
  let visibility = r.height > height ? true : false;
343
+
344
+ if (debugHash) {
345
+ console.group(`[Heatmap visible] hash=${debugHash} tag=${el?.tagName}`);
346
+ console.log(` rect: top=${r.top.toFixed(1)} bottom=${r.bottom.toFixed(1)} left=${r.left.toFixed(1)} width=${r.width.toFixed(1)} height=${r.height.toFixed(1)}`);
347
+ console.log(` viewport height=${height} | r.height > height? ${r.height > height}`);
348
+ }
349
+
313
350
  if (visibility === false && r.width > 0 && r.height > 0) {
351
+ const checkX = r.left + (r.width / 2);
352
+ const checkY = r.top + (r.height / 2);
353
+ if (debugHash) { console.log(` elementsFromPoint(${checkX.toFixed(1)}, ${checkY.toFixed(1)})`); }
354
+
314
355
  while (!visibility && doc)
315
356
  {
316
357
  let shadowElement = null;
317
- let elements = doc.elementsFromPoint(r.left + (r.width / 2), r.top + (r.height / 2));
358
+ let elements = doc.elementsFromPoint(checkX, checkY);
359
+
360
+ if (debugHash) {
361
+ const topElements = elements.slice(0, 5).map(e => `${e.tagName}${e.id ? '#' + e.id : ''}${e.className ? '.' + String(e.className).trim().split(/\s+/).slice(0, 2).join('.') : ''}`);
362
+ console.log(` top elements at center: [${topElements.join(', ')}]`);
363
+ console.log(` is el in top elements? ${elements.includes(el)} | el matches first non-canvas? ${elements.find(e => !(e.tagName === Constant.Canvas || (e.id && e.id.indexOf(Constant.ClarityPrefix) === 0))) === el}`);
364
+ }
365
+
318
366
  for (let e of elements) {
319
367
  // Ignore if top element ends up being the canvas element we added for heatmap visualization
320
368
  if (e.tagName === Constant.Canvas || (e.id && e.id.indexOf(Constant.ClarityPrefix) === 0)) { continue; }
@@ -324,8 +372,19 @@ export class HeatmapHelper {
324
372
  }
325
373
  doc = shadowElement;
326
374
  }
375
+ } else if (debugHash) {
376
+ if (r.width === 0 || r.height === 0) {
377
+ console.log(` SKIP elementsFromPoint — zero dimension: width=${r.width} height=${r.height}`);
378
+ }
379
+ }
380
+
381
+ const result = visibility && r.bottom >= 0 && r.top <= height;
382
+ if (debugHash) {
383
+ console.log(` visibility=${visibility} | r.bottom(${r.bottom.toFixed(1)}) >= 0? ${r.bottom >= 0} | r.top(${r.top.toFixed(1)}) <= height(${height})? ${r.top <= height}`);
384
+ console.log(` => final result: ${result}`);
385
+ console.groupEnd();
327
386
  }
328
- return visibility && r.bottom >= 0 && r.top <= height;
387
+ return result;
329
388
  }
330
389
 
331
390
  private waitForDialogs = async (): Promise<void> => {