@midscene/recorder 1.0.2 → 1.0.3-beta-20251221011051.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -114,7 +114,7 @@
114
114
  }
115
115
  function generateHashId(type, elementRect) {
116
116
  const rectStr = elementRect ? `${elementRect.left}_${elementRect.top}_${elementRect.width}_${elementRect.height}${void 0 !== elementRect.x ? `_${elementRect.x}` : ''}${void 0 !== elementRect.y ? `_${elementRect.y}` : ''}` : 'no_rect';
117
- const combined = `${type}_${rectStr}`;
117
+ const combined = `${type}_${rectStr}_${Date.now()}`;
118
118
  let hash = 0;
119
119
  for(let i = 0; i < combined.length; i++){
120
120
  const char = combined.charCodeAt(i);
@@ -131,16 +131,19 @@
131
131
  if ('click' === event.type && event.isLabelClick) return event;
132
132
  }
133
133
  };
134
+ function isElementScrollable(el) {
135
+ const style = window.getComputedStyle(el);
136
+ const overflowY = style.overflowY;
137
+ const overflowX = style.overflowX;
138
+ const isScrollableY = ('auto' === overflowY || 'scroll' === overflowY) && el.scrollHeight > el.clientHeight;
139
+ const isScrollableX = ('auto' === overflowX || 'scroll' === overflowX) && el.scrollWidth > el.clientWidth;
140
+ return isScrollableY || isScrollableX;
141
+ }
134
142
  function getAllScrollableElements() {
135
143
  const elements = [];
136
144
  const all = document.querySelectorAll('body *');
137
145
  all.forEach((el)=>{
138
- const style = window.getComputedStyle(el);
139
- const overflowY = style.overflowY;
140
- const overflowX = style.overflowX;
141
- const isScrollableY = ('auto' === overflowY || 'scroll' === overflowY) && el.scrollHeight > el.clientHeight;
142
- const isScrollableX = ('auto' === overflowX || 'scroll' === overflowX) && el.scrollWidth > el.clientWidth;
143
- if (isScrollableY || isScrollableX) elements.push(el);
146
+ if (isElementScrollable(el)) elements.push(el);
144
147
  });
145
148
  return elements;
146
149
  }
@@ -154,6 +157,7 @@
154
157
  lastViewportScroll = null;
155
158
  scrollTargets = [];
156
159
  sessionId;
160
+ mutationObserver = null;
157
161
  constructor(eventCallback, sessionId){
158
162
  this.eventCallback = eventCallback;
159
163
  this.sessionId = sessionId;
@@ -171,6 +175,33 @@
171
175
  hashId: `navigation_${Date.now()}`
172
176
  };
173
177
  }
178
+ checkAndAddScrollListeners(element) {
179
+ if (isElementScrollable(element) && !this.scrollTargets.includes(element)) {
180
+ element.addEventListener('scroll', this.handleScroll, {
181
+ passive: true
182
+ });
183
+ this.scrollTargets.push(element);
184
+ debugLog('Added scroll listener to dynamic element:', element);
185
+ }
186
+ const descendants = element.querySelectorAll('*');
187
+ descendants.forEach((descendant)=>{
188
+ if (isElementScrollable(descendant) && !this.scrollTargets.includes(descendant)) {
189
+ descendant.addEventListener('scroll', this.handleScroll, {
190
+ passive: true
191
+ });
192
+ this.scrollTargets.push(descendant);
193
+ debugLog('Added scroll listener to dynamic descendant:', descendant);
194
+ }
195
+ });
196
+ }
197
+ handleMutations = (mutations)=>{
198
+ if (!this.isRecording) return;
199
+ mutations.forEach((mutation)=>{
200
+ mutation.addedNodes.forEach((node)=>{
201
+ if (node instanceof HTMLElement) this.checkAndAddScrollListeners(node);
202
+ });
203
+ });
204
+ };
174
205
  start() {
175
206
  if (this.isRecording) return void debugLog('Recording already active, ignoring start request');
176
207
  this.isRecording = true;
@@ -196,6 +227,12 @@
196
227
  passive: true
197
228
  });
198
229
  });
230
+ this.mutationObserver = new MutationObserver(this.handleMutations);
231
+ this.mutationObserver.observe(document.body, {
232
+ childList: true,
233
+ subtree: true
234
+ });
235
+ debugLog('MutationObserver started to detect dynamic scrollable elements');
199
236
  }
200
237
  stop() {
201
238
  if (!this.isRecording) return void debugLog('Recording not active, ignoring stop request');
@@ -214,6 +251,11 @@
214
251
  this.scrollTargets.forEach((target)=>{
215
252
  target.removeEventListener('scroll', this.handleScroll);
216
253
  });
254
+ if (this.mutationObserver) {
255
+ this.mutationObserver.disconnect();
256
+ this.mutationObserver = null;
257
+ debugLog('MutationObserver stopped');
258
+ }
217
259
  debugLog('Removed all event listeners');
218
260
  }
219
261
  handleClick = (event)=>{
package/dist/recorder.js CHANGED
@@ -5,7 +5,7 @@ function debugLog(...args) {
5
5
  }
6
6
  function generateHashId(type, elementRect) {
7
7
  const rectStr = elementRect ? `${elementRect.left}_${elementRect.top}_${elementRect.width}_${elementRect.height}${void 0 !== elementRect.x ? `_${elementRect.x}` : ''}${void 0 !== elementRect.y ? `_${elementRect.y}` : ''}` : 'no_rect';
8
- const combined = `${type}_${rectStr}`;
8
+ const combined = `${type}_${rectStr}_${Date.now()}`;
9
9
  let hash = 0;
10
10
  for(let i = 0; i < combined.length; i++){
11
11
  const char = combined.charCodeAt(i);
@@ -22,16 +22,19 @@ const getLastLabelClick = (events)=>{
22
22
  if ('click' === event.type && event.isLabelClick) return event;
23
23
  }
24
24
  };
25
+ function isElementScrollable(el) {
26
+ const style = window.getComputedStyle(el);
27
+ const overflowY = style.overflowY;
28
+ const overflowX = style.overflowX;
29
+ const isScrollableY = ('auto' === overflowY || 'scroll' === overflowY) && el.scrollHeight > el.clientHeight;
30
+ const isScrollableX = ('auto' === overflowX || 'scroll' === overflowX) && el.scrollWidth > el.clientWidth;
31
+ return isScrollableY || isScrollableX;
32
+ }
25
33
  function getAllScrollableElements() {
26
34
  const elements = [];
27
35
  const all = document.querySelectorAll('body *');
28
36
  all.forEach((el)=>{
29
- const style = window.getComputedStyle(el);
30
- const overflowY = style.overflowY;
31
- const overflowX = style.overflowX;
32
- const isScrollableY = ('auto' === overflowY || 'scroll' === overflowY) && el.scrollHeight > el.clientHeight;
33
- const isScrollableX = ('auto' === overflowX || 'scroll' === overflowX) && el.scrollWidth > el.clientWidth;
34
- if (isScrollableY || isScrollableX) elements.push(el);
37
+ if (isElementScrollable(el)) elements.push(el);
35
38
  });
36
39
  return elements;
37
40
  }
@@ -45,6 +48,7 @@ class EventRecorder {
45
48
  lastViewportScroll = null;
46
49
  scrollTargets = [];
47
50
  sessionId;
51
+ mutationObserver = null;
48
52
  constructor(eventCallback, sessionId){
49
53
  this.eventCallback = eventCallback;
50
54
  this.sessionId = sessionId;
@@ -62,6 +66,33 @@ class EventRecorder {
62
66
  hashId: `navigation_${Date.now()}`
63
67
  };
64
68
  }
69
+ checkAndAddScrollListeners(element) {
70
+ if (isElementScrollable(element) && !this.scrollTargets.includes(element)) {
71
+ element.addEventListener('scroll', this.handleScroll, {
72
+ passive: true
73
+ });
74
+ this.scrollTargets.push(element);
75
+ debugLog('Added scroll listener to dynamic element:', element);
76
+ }
77
+ const descendants = element.querySelectorAll('*');
78
+ descendants.forEach((descendant)=>{
79
+ if (isElementScrollable(descendant) && !this.scrollTargets.includes(descendant)) {
80
+ descendant.addEventListener('scroll', this.handleScroll, {
81
+ passive: true
82
+ });
83
+ this.scrollTargets.push(descendant);
84
+ debugLog('Added scroll listener to dynamic descendant:', descendant);
85
+ }
86
+ });
87
+ }
88
+ handleMutations = (mutations)=>{
89
+ if (!this.isRecording) return;
90
+ mutations.forEach((mutation)=>{
91
+ mutation.addedNodes.forEach((node)=>{
92
+ if (node instanceof HTMLElement) this.checkAndAddScrollListeners(node);
93
+ });
94
+ });
95
+ };
65
96
  start() {
66
97
  if (this.isRecording) return void debugLog('Recording already active, ignoring start request');
67
98
  this.isRecording = true;
@@ -87,6 +118,12 @@ class EventRecorder {
87
118
  passive: true
88
119
  });
89
120
  });
121
+ this.mutationObserver = new MutationObserver(this.handleMutations);
122
+ this.mutationObserver.observe(document.body, {
123
+ childList: true,
124
+ subtree: true
125
+ });
126
+ debugLog('MutationObserver started to detect dynamic scrollable elements');
90
127
  }
91
128
  stop() {
92
129
  if (!this.isRecording) return void debugLog('Recording not active, ignoring stop request');
@@ -105,6 +142,11 @@ class EventRecorder {
105
142
  this.scrollTargets.forEach((target)=>{
106
143
  target.removeEventListener('scroll', this.handleScroll);
107
144
  });
145
+ if (this.mutationObserver) {
146
+ this.mutationObserver.disconnect();
147
+ this.mutationObserver = null;
148
+ debugLog('MutationObserver stopped');
149
+ }
108
150
  debugLog('Removed all event listeners');
109
151
  }
110
152
  handleClick = (event)=>{
@@ -49,8 +49,11 @@ export declare class EventRecorder {
49
49
  private lastViewportScroll;
50
50
  private scrollTargets;
51
51
  private sessionId;
52
+ private mutationObserver;
52
53
  constructor(eventCallback: EventCallback, sessionId: string);
53
54
  createNavigationEvent(url: string, title: string): ChromeRecordedEvent;
55
+ private checkAndAddScrollListeners;
56
+ private handleMutations;
54
57
  start(): void;
55
58
  stop(): void;
56
59
  private handleClick;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midscene/recorder",
3
- "version": "1.0.2",
3
+ "version": "1.0.3-beta-20251221011051.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -24,7 +24,7 @@
24
24
  "antd": "^5.21.6",
25
25
  "dayjs": "^1.11.11",
26
26
  "react-dom": "18.3.1",
27
- "@midscene/shared": "1.0.2"
27
+ "@midscene/shared": "1.0.3-beta-20251221011051.0"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": "18.3.1",