@sqaitech/recorder 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +23 -0
- package/dist/Button.js +19 -0
- package/dist/RecordTimeline.css +27 -0
- package/dist/RecordTimeline.js +452 -0
- package/dist/button.css +35 -0
- package/dist/components/shiny-text.css +73 -0
- package/dist/components/shiny-text.js +13 -0
- package/dist/index.js +4 -0
- package/dist/recorder-iife-index.js +2 -0
- package/dist/recorder-iife.js +442 -0
- package/dist/recorder.js +348 -0
- package/dist/types/src/Button.d.ts +10 -0
- package/dist/types/src/RecordTimeline.d.ts +8 -0
- package/dist/types/src/components/shiny-text.d.ts +12 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/recorder-iife-index.d.ts +6 -0
- package/dist/types/src/recorder.d.ts +64 -0
- package/package.json +38 -0
package/dist/recorder.js
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { getElementXpath, isNotContainerElement } from "@sqaitech/shared/extractor";
|
|
2
|
+
const DEBUG = 'true' === localStorage.getItem('DEBUG');
|
|
3
|
+
function debugLog(...args) {
|
|
4
|
+
if (DEBUG) console.log('[EventRecorder]', ...args);
|
|
5
|
+
}
|
|
6
|
+
function generateHashId(type, elementRect) {
|
|
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}`;
|
|
9
|
+
let hash = 0;
|
|
10
|
+
for(let i = 0; i < combined.length; i++){
|
|
11
|
+
const char = combined.charCodeAt(i);
|
|
12
|
+
hash = (hash << 5) - hash + char;
|
|
13
|
+
hash &= hash;
|
|
14
|
+
}
|
|
15
|
+
return Math.abs(hash).toString(36);
|
|
16
|
+
}
|
|
17
|
+
const isSameInputTarget = (event1, event2)=>event1.element === event2.element;
|
|
18
|
+
const isSameScrollTarget = (event1, event2)=>event1.element === event2.element;
|
|
19
|
+
const getLastLabelClick = (events)=>{
|
|
20
|
+
for(let i = events.length - 1; i >= 0; i--){
|
|
21
|
+
const event = events[i];
|
|
22
|
+
if ('click' === event.type && event.isLabelClick) return event;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
function getAllScrollableElements() {
|
|
26
|
+
const elements = [];
|
|
27
|
+
const all = document.querySelectorAll('body *');
|
|
28
|
+
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);
|
|
35
|
+
});
|
|
36
|
+
return elements;
|
|
37
|
+
}
|
|
38
|
+
class EventRecorder {
|
|
39
|
+
isRecording = false;
|
|
40
|
+
eventCallback;
|
|
41
|
+
scrollThrottleTimer = null;
|
|
42
|
+
scrollThrottleDelay = 200;
|
|
43
|
+
inputThrottleTimer = null;
|
|
44
|
+
inputThrottleDelay = 300;
|
|
45
|
+
lastViewportScroll = null;
|
|
46
|
+
scrollTargets = [];
|
|
47
|
+
sessionId;
|
|
48
|
+
constructor(eventCallback, sessionId){
|
|
49
|
+
this.eventCallback = eventCallback;
|
|
50
|
+
this.sessionId = sessionId;
|
|
51
|
+
}
|
|
52
|
+
createNavigationEvent(url, title) {
|
|
53
|
+
return {
|
|
54
|
+
type: 'navigation',
|
|
55
|
+
url,
|
|
56
|
+
title,
|
|
57
|
+
pageInfo: {
|
|
58
|
+
width: window.innerWidth,
|
|
59
|
+
height: window.innerHeight
|
|
60
|
+
},
|
|
61
|
+
timestamp: Date.now(),
|
|
62
|
+
hashId: `navigation_${Date.now()}`
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
start() {
|
|
66
|
+
if (this.isRecording) return void debugLog('Recording already active, ignoring start request');
|
|
67
|
+
this.isRecording = true;
|
|
68
|
+
debugLog('Starting event recording');
|
|
69
|
+
this.scrollTargets = [];
|
|
70
|
+
if (0 === this.scrollTargets.length) {
|
|
71
|
+
this.scrollTargets = getAllScrollableElements();
|
|
72
|
+
this.scrollTargets.push(document.body);
|
|
73
|
+
}
|
|
74
|
+
debugLog('Added event listeners for', this.scrollTargets.length, 'scroll targets');
|
|
75
|
+
setTimeout(()=>{
|
|
76
|
+
const navigationEvent = this.createNavigationEvent(window.location.href, document.title);
|
|
77
|
+
this.eventCallback(navigationEvent);
|
|
78
|
+
debugLog('Added final navigation event', navigationEvent);
|
|
79
|
+
}, 0);
|
|
80
|
+
document.addEventListener('click', this.handleClick, true);
|
|
81
|
+
document.addEventListener('input', this.handleInput);
|
|
82
|
+
document.addEventListener('scroll', this.handleScroll, {
|
|
83
|
+
passive: true
|
|
84
|
+
});
|
|
85
|
+
this.scrollTargets.forEach((target)=>{
|
|
86
|
+
target.addEventListener('scroll', this.handleScroll, {
|
|
87
|
+
passive: true
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
stop() {
|
|
92
|
+
if (!this.isRecording) return void debugLog('Recording not active, ignoring stop request');
|
|
93
|
+
this.isRecording = false;
|
|
94
|
+
debugLog('Stopping event recording');
|
|
95
|
+
if (this.scrollThrottleTimer) {
|
|
96
|
+
clearTimeout(this.scrollThrottleTimer);
|
|
97
|
+
this.scrollThrottleTimer = null;
|
|
98
|
+
}
|
|
99
|
+
if (this.inputThrottleTimer) {
|
|
100
|
+
clearTimeout(this.inputThrottleTimer);
|
|
101
|
+
this.inputThrottleTimer = null;
|
|
102
|
+
}
|
|
103
|
+
document.removeEventListener('click', this.handleClick);
|
|
104
|
+
document.removeEventListener('input', this.handleInput);
|
|
105
|
+
this.scrollTargets.forEach((target)=>{
|
|
106
|
+
target.removeEventListener('scroll', this.handleScroll);
|
|
107
|
+
});
|
|
108
|
+
debugLog('Removed all event listeners');
|
|
109
|
+
}
|
|
110
|
+
handleClick = (event)=>{
|
|
111
|
+
if (!this.isRecording) return;
|
|
112
|
+
const target = event.target;
|
|
113
|
+
const { isLabelClick, labelInfo } = this.checkLabelClick(target);
|
|
114
|
+
const rect = target.getBoundingClientRect();
|
|
115
|
+
const elementRect = {
|
|
116
|
+
x: Number(event.clientX.toFixed(2)),
|
|
117
|
+
y: Number(event.clientY.toFixed(2))
|
|
118
|
+
};
|
|
119
|
+
console.log('isNotContainerElement', isNotContainerElement(target));
|
|
120
|
+
if (isNotContainerElement(target)) {
|
|
121
|
+
elementRect.left = Number(rect.left.toFixed(2));
|
|
122
|
+
elementRect.top = Number(rect.top.toFixed(2));
|
|
123
|
+
elementRect.width = Number(rect.width.toFixed(2));
|
|
124
|
+
elementRect.height = Number(rect.height.toFixed(2));
|
|
125
|
+
}
|
|
126
|
+
const clickEvent = {
|
|
127
|
+
type: 'click',
|
|
128
|
+
elementRect,
|
|
129
|
+
pageInfo: {
|
|
130
|
+
width: window.innerWidth,
|
|
131
|
+
height: window.innerHeight
|
|
132
|
+
},
|
|
133
|
+
value: '',
|
|
134
|
+
timestamp: Date.now(),
|
|
135
|
+
hashId: generateHashId('click', {
|
|
136
|
+
...elementRect
|
|
137
|
+
}),
|
|
138
|
+
element: target,
|
|
139
|
+
isLabelClick,
|
|
140
|
+
labelInfo,
|
|
141
|
+
isTrusted: event.isTrusted,
|
|
142
|
+
detail: event.detail
|
|
143
|
+
};
|
|
144
|
+
this.eventCallback(clickEvent);
|
|
145
|
+
};
|
|
146
|
+
handleScroll = (event)=>{
|
|
147
|
+
if (!this.isRecording) return;
|
|
148
|
+
function isDocument(target) {
|
|
149
|
+
return target instanceof Document;
|
|
150
|
+
}
|
|
151
|
+
const target = event.target;
|
|
152
|
+
const scrollXTarget = isDocument(target) ? window.scrollX : target.scrollLeft;
|
|
153
|
+
const scrollYTarget = isDocument(target) ? window.scrollY : target.scrollTop;
|
|
154
|
+
const rect = isDocument(target) ? {
|
|
155
|
+
left: 0,
|
|
156
|
+
top: 0,
|
|
157
|
+
width: window.innerWidth,
|
|
158
|
+
height: window.innerHeight
|
|
159
|
+
} : target.getBoundingClientRect();
|
|
160
|
+
if (this.scrollThrottleTimer) clearTimeout(this.scrollThrottleTimer);
|
|
161
|
+
this.scrollThrottleTimer = window.setTimeout(()=>{
|
|
162
|
+
if (this.isRecording) {
|
|
163
|
+
const elementRect = {
|
|
164
|
+
left: isDocument(target) ? 0 : Number(rect.left.toFixed(2)),
|
|
165
|
+
top: isDocument(target) ? 0 : Number(rect.top.toFixed(2)),
|
|
166
|
+
width: isDocument(target) ? window.innerWidth : Number(rect.width.toFixed(2)),
|
|
167
|
+
height: isDocument(target) ? window.innerHeight : Number(rect.height.toFixed(2))
|
|
168
|
+
};
|
|
169
|
+
const scrollEvent = {
|
|
170
|
+
type: 'scroll',
|
|
171
|
+
elementRect,
|
|
172
|
+
pageInfo: {
|
|
173
|
+
width: window.innerWidth,
|
|
174
|
+
height: window.innerHeight
|
|
175
|
+
},
|
|
176
|
+
value: `${scrollXTarget.toFixed(2)},${scrollYTarget.toFixed(2)}`,
|
|
177
|
+
timestamp: Date.now(),
|
|
178
|
+
hashId: generateHashId('scroll', {
|
|
179
|
+
...elementRect
|
|
180
|
+
}),
|
|
181
|
+
element: target
|
|
182
|
+
};
|
|
183
|
+
this.eventCallback(scrollEvent);
|
|
184
|
+
}
|
|
185
|
+
this.scrollThrottleTimer = null;
|
|
186
|
+
}, this.scrollThrottleDelay);
|
|
187
|
+
};
|
|
188
|
+
handleInput = (event)=>{
|
|
189
|
+
if (!this.isRecording) return;
|
|
190
|
+
const target = event.target;
|
|
191
|
+
if ('checkbox' === target.type) return;
|
|
192
|
+
const rect = target.getBoundingClientRect();
|
|
193
|
+
const elementRect = {
|
|
194
|
+
left: Number(rect.left.toFixed(2)),
|
|
195
|
+
top: Number(rect.top.toFixed(2)),
|
|
196
|
+
width: Number(rect.width.toFixed(2)),
|
|
197
|
+
height: Number(rect.height.toFixed(2))
|
|
198
|
+
};
|
|
199
|
+
if (this.inputThrottleTimer) clearTimeout(this.inputThrottleTimer);
|
|
200
|
+
this.inputThrottleTimer = window.setTimeout(()=>{
|
|
201
|
+
if (this.isRecording) {
|
|
202
|
+
const inputEvent = {
|
|
203
|
+
type: 'input',
|
|
204
|
+
value: 'password' !== target.type ? target.value : '*****',
|
|
205
|
+
timestamp: Date.now(),
|
|
206
|
+
hashId: generateHashId('input', {
|
|
207
|
+
...elementRect
|
|
208
|
+
}),
|
|
209
|
+
element: target,
|
|
210
|
+
inputType: target.type || 'text',
|
|
211
|
+
elementRect,
|
|
212
|
+
pageInfo: {
|
|
213
|
+
width: window.innerWidth,
|
|
214
|
+
height: window.innerHeight
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
debugLog('Throttled input event:', {
|
|
218
|
+
value: inputEvent.value,
|
|
219
|
+
timestamp: inputEvent.timestamp,
|
|
220
|
+
target: target.tagName,
|
|
221
|
+
inputType: target.type
|
|
222
|
+
});
|
|
223
|
+
this.eventCallback(inputEvent);
|
|
224
|
+
}
|
|
225
|
+
this.inputThrottleTimer = null;
|
|
226
|
+
}, this.inputThrottleDelay);
|
|
227
|
+
};
|
|
228
|
+
checkLabelClick(target) {
|
|
229
|
+
let isLabelClick = false;
|
|
230
|
+
let labelInfo;
|
|
231
|
+
if (target) if ('LABEL' === target.tagName) {
|
|
232
|
+
isLabelClick = true;
|
|
233
|
+
labelInfo = {
|
|
234
|
+
htmlFor: target.htmlFor,
|
|
235
|
+
textContent: target.textContent?.trim(),
|
|
236
|
+
xpath: getElementXpath(target)
|
|
237
|
+
};
|
|
238
|
+
} else {
|
|
239
|
+
let parent = target.parentElement;
|
|
240
|
+
while(parent){
|
|
241
|
+
if ('LABEL' === parent.tagName) {
|
|
242
|
+
isLabelClick = true;
|
|
243
|
+
labelInfo = {
|
|
244
|
+
htmlFor: parent.htmlFor,
|
|
245
|
+
textContent: parent.textContent?.trim(),
|
|
246
|
+
xpath: getElementXpath(parent)
|
|
247
|
+
};
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
parent = parent.parentElement;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return {
|
|
254
|
+
isLabelClick,
|
|
255
|
+
labelInfo
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
isActive() {
|
|
259
|
+
return this.isRecording;
|
|
260
|
+
}
|
|
261
|
+
optimizeEvent(event, events) {
|
|
262
|
+
const lastEvent = events[events.length - 1];
|
|
263
|
+
if ('click' === event.type) {
|
|
264
|
+
const lastEvent = getLastLabelClick(events);
|
|
265
|
+
if (event.element) {
|
|
266
|
+
const { isLabelClick, labelInfo } = this.checkLabelClick(event.element);
|
|
267
|
+
if (lastEvent && isLabelClick && 'click' === lastEvent.type && lastEvent.isLabelClick && (lastEvent.labelInfo?.htmlFor && event.element.id && lastEvent.labelInfo?.htmlFor === event.element.id || labelInfo?.xpath && lastEvent.labelInfo?.xpath && lastEvent.labelInfo?.xpath === labelInfo?.xpath)) {
|
|
268
|
+
debugLog('Skip input event triggered by label click:', event.element);
|
|
269
|
+
return events;
|
|
270
|
+
}
|
|
271
|
+
return [
|
|
272
|
+
...events,
|
|
273
|
+
event
|
|
274
|
+
];
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if ('input' === event.type) {
|
|
278
|
+
if (lastEvent && 'click' === lastEvent.type && lastEvent.isLabelClick && lastEvent.labelInfo?.htmlFor === event.targetId) {
|
|
279
|
+
debugLog('Skipping input event - triggered by label click:', {
|
|
280
|
+
labelHtmlFor: getLastLabelClick(events)?.labelInfo?.htmlFor,
|
|
281
|
+
inputId: event.targetId,
|
|
282
|
+
element: event.element
|
|
283
|
+
});
|
|
284
|
+
return events;
|
|
285
|
+
}
|
|
286
|
+
if (lastEvent && 'input' === lastEvent.type && isSameInputTarget(lastEvent, event)) {
|
|
287
|
+
const oldInputEvent = events[events.length - 1];
|
|
288
|
+
const newEvents = [
|
|
289
|
+
...events
|
|
290
|
+
];
|
|
291
|
+
newEvents[events.length - 1] = {
|
|
292
|
+
value: event.element?.value,
|
|
293
|
+
...event
|
|
294
|
+
};
|
|
295
|
+
debugLog('Merging input event:', {
|
|
296
|
+
oldValue: oldInputEvent.value,
|
|
297
|
+
newValue: event.value,
|
|
298
|
+
oldTimestamp: oldInputEvent.timestamp,
|
|
299
|
+
newTimestamp: event.timestamp,
|
|
300
|
+
target: event.targetTagName
|
|
301
|
+
});
|
|
302
|
+
return newEvents;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if ('scroll' === event.type) {
|
|
306
|
+
if (lastEvent && 'scroll' === lastEvent.type && isSameScrollTarget(lastEvent, event)) {
|
|
307
|
+
const oldScrollEvent = events[events.length - 1];
|
|
308
|
+
const newEvents = [
|
|
309
|
+
...events
|
|
310
|
+
];
|
|
311
|
+
newEvents[events.length - 1] = event;
|
|
312
|
+
debugLog('Replacing last scroll event with new scroll event:', {
|
|
313
|
+
oldPosition: `${oldScrollEvent.elementRect?.left},${oldScrollEvent.elementRect?.top}`,
|
|
314
|
+
newPosition: `${event.elementRect?.left},${event.elementRect?.top}`,
|
|
315
|
+
oldTimestamp: oldScrollEvent.timestamp,
|
|
316
|
+
newTimestamp: event.timestamp,
|
|
317
|
+
target: event.targetTagName
|
|
318
|
+
});
|
|
319
|
+
return newEvents;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return [
|
|
323
|
+
...events,
|
|
324
|
+
event
|
|
325
|
+
];
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function convertToChromeEvent(event) {
|
|
329
|
+
return {
|
|
330
|
+
type: event.type,
|
|
331
|
+
url: event.url,
|
|
332
|
+
title: event.title,
|
|
333
|
+
value: event.value,
|
|
334
|
+
elementRect: event.elementRect,
|
|
335
|
+
pageInfo: event.pageInfo,
|
|
336
|
+
screenshotBefore: event.screenshotBefore,
|
|
337
|
+
screenshotAfter: event.screenshotAfter,
|
|
338
|
+
elementDescription: event.elementDescription,
|
|
339
|
+
descriptionLoading: event.descriptionLoading,
|
|
340
|
+
screenshotWithBox: event.screenshotWithBox,
|
|
341
|
+
timestamp: event.timestamp,
|
|
342
|
+
hashId: event.hashId
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function convertToChromeEvents(events) {
|
|
346
|
+
return events.map(convertToChromeEvent);
|
|
347
|
+
}
|
|
348
|
+
export { EventRecorder, convertToChromeEvent, convertToChromeEvents };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import './button.css';
|
|
2
|
+
interface ButtonProps {
|
|
3
|
+
primary?: boolean;
|
|
4
|
+
backgroundColor?: string;
|
|
5
|
+
size?: 'small' | 'medium' | 'large';
|
|
6
|
+
label: string;
|
|
7
|
+
onClick?: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare const Button: ({ primary, size, backgroundColor, label, ...props }: ButtonProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RecordedEvent } from './recorder';
|
|
2
|
+
import './RecordTimeline.css';
|
|
3
|
+
interface RecordTimelineProps {
|
|
4
|
+
events: RecordedEvent[];
|
|
5
|
+
onEventClick?: (event: RecordedEvent, index: number) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare const RecordTimeline: ({ events, onEventClick, }: RecordTimelineProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import './shiny-text.css';
|
|
3
|
+
type ColorTheme = 'blue' | 'purple' | 'green' | 'rainbow';
|
|
4
|
+
interface ShinyTextProps {
|
|
5
|
+
text: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
speed?: number;
|
|
8
|
+
className?: string;
|
|
9
|
+
colorTheme?: ColorTheme;
|
|
10
|
+
}
|
|
11
|
+
export declare const ShinyText: React.FC<ShinyTextProps>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export interface ChromeRecordedEvent {
|
|
2
|
+
type: 'click' | 'scroll' | 'input' | 'navigation' | 'setViewport' | 'keydown';
|
|
3
|
+
url?: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
value?: string;
|
|
6
|
+
elementRect?: {
|
|
7
|
+
left?: number;
|
|
8
|
+
top?: number;
|
|
9
|
+
width?: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
x?: number;
|
|
12
|
+
y?: number;
|
|
13
|
+
};
|
|
14
|
+
pageInfo: {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
screenshotBefore?: string;
|
|
19
|
+
screenshotAfter?: string;
|
|
20
|
+
elementDescription?: string;
|
|
21
|
+
descriptionLoading?: boolean;
|
|
22
|
+
screenshotWithBox?: string;
|
|
23
|
+
timestamp: number;
|
|
24
|
+
hashId: string;
|
|
25
|
+
}
|
|
26
|
+
export interface RecordedEvent extends ChromeRecordedEvent {
|
|
27
|
+
element?: HTMLElement;
|
|
28
|
+
targetTagName?: string;
|
|
29
|
+
targetId?: string;
|
|
30
|
+
targetClassName?: string;
|
|
31
|
+
isLabelClick?: boolean;
|
|
32
|
+
labelInfo?: {
|
|
33
|
+
htmlFor?: string;
|
|
34
|
+
textContent?: string;
|
|
35
|
+
xpath?: string;
|
|
36
|
+
};
|
|
37
|
+
isTrusted?: boolean;
|
|
38
|
+
detail?: number;
|
|
39
|
+
inputType?: string;
|
|
40
|
+
}
|
|
41
|
+
export type EventCallback = (event: RecordedEvent) => void;
|
|
42
|
+
export declare class EventRecorder {
|
|
43
|
+
private isRecording;
|
|
44
|
+
private eventCallback;
|
|
45
|
+
private scrollThrottleTimer;
|
|
46
|
+
private scrollThrottleDelay;
|
|
47
|
+
private inputThrottleTimer;
|
|
48
|
+
private inputThrottleDelay;
|
|
49
|
+
private lastViewportScroll;
|
|
50
|
+
private scrollTargets;
|
|
51
|
+
private sessionId;
|
|
52
|
+
constructor(eventCallback: EventCallback, sessionId: string);
|
|
53
|
+
createNavigationEvent(url: string, title: string): ChromeRecordedEvent;
|
|
54
|
+
start(): void;
|
|
55
|
+
stop(): void;
|
|
56
|
+
private handleClick;
|
|
57
|
+
private handleScroll;
|
|
58
|
+
private handleInput;
|
|
59
|
+
private checkLabelClick;
|
|
60
|
+
isActive(): boolean;
|
|
61
|
+
optimizeEvent(event: RecordedEvent, events: RecordedEvent[]): RecordedEvent[];
|
|
62
|
+
}
|
|
63
|
+
export declare function convertToChromeEvent(event: RecordedEvent): ChromeRecordedEvent;
|
|
64
|
+
export declare function convertToChromeEvents(events: RecordedEvent[]): ChromeRecordedEvent[];
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sqaitech/recorder",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/types/src/index.d.ts",
|
|
8
|
+
"import": "./dist/index.js"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"types": "./dist/types/src/index.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@rsbuild/plugin-react": "^1.3.1",
|
|
17
|
+
"@rslib/core": "^0.11.2",
|
|
18
|
+
"@types/react": "^18.3.1",
|
|
19
|
+
"react": "18.3.1",
|
|
20
|
+
"typescript": "^5.8.3"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@ant-design/icons": "^5.3.1",
|
|
24
|
+
"antd": "^5.21.6",
|
|
25
|
+
"dayjs": "^1.11.11",
|
|
26
|
+
"react-dom": "18.3.1",
|
|
27
|
+
"@sqaitech/shared": "0.5.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": "18.3.1",
|
|
31
|
+
"react-dom": "18.3.1"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "rslib build",
|
|
35
|
+
"dev": "rslib build --watch",
|
|
36
|
+
"build:watch": "npm run dev"
|
|
37
|
+
}
|
|
38
|
+
}
|