@rpascene/recorder 0.30.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Button.js ADDED
@@ -0,0 +1,19 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import "./button.css";
3
+ const Button = ({ primary = false, size = 'medium', backgroundColor, label, ...props })=>{
4
+ const mode = primary ? 'demo-button--primary' : 'demo-button--secondary';
5
+ return /*#__PURE__*/ jsx("button", {
6
+ type: "button",
7
+ className: [
8
+ 'demo-button',
9
+ `demo-button--${size}`,
10
+ mode
11
+ ].join(' '),
12
+ style: {
13
+ backgroundColor
14
+ },
15
+ ...props,
16
+ children: label
17
+ });
18
+ };
19
+ export { Button };
@@ -0,0 +1,27 @@
1
+ .record-timeline-screenshot-thumbnail {
2
+ transition: transform .2s ease-in-out, box-shadow .2s ease-in-out;
3
+ }
4
+
5
+ .record-timeline-screenshot-thumbnail:hover {
6
+ transform: scale(1.05);
7
+ box-shadow: 0 4px 12px #00000026;
8
+ }
9
+
10
+ .record-timeline-screenshot-popover {
11
+ max-width: 600px;
12
+ }
13
+
14
+ .record-timeline-screenshot-popover .ant-popover-inner {
15
+ padding: 8px;
16
+ }
17
+
18
+ .record-timeline-screenshot-full {
19
+ border-radius: 4px;
20
+ box-shadow: 0 2px 8px #0000001a;
21
+ }
22
+
23
+ .timeline-scrollable > div {
24
+ max-height: 500px;
25
+ overflow-y: auto;
26
+ }
27
+
@@ -0,0 +1,452 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { AimOutlined, CompassOutlined, CopyOutlined, EditOutlined, KeyOutlined, VerticalAlignTopOutlined } from "@ant-design/icons";
3
+ import { Button, Card, Image, Space, Timeline, Typography, message } from "antd";
4
+ import { useEffect, useState } from "react";
5
+ import { ShinyText } from "./components/shiny-text.js";
6
+ import "./RecordTimeline.css";
7
+ const { Text } = Typography;
8
+ const RecordTimeline = ({ events, onEventClick })=>{
9
+ const [expandedEvents, setExpandedEvents] = useState(new Set());
10
+ console.log('events', events);
11
+ useEffect(()=>{
12
+ if (events.length > 0) {
13
+ const timeline = document.querySelector('.ant-timeline');
14
+ if (timeline) timeline.scrollIntoView({
15
+ behavior: 'smooth',
16
+ block: 'end'
17
+ });
18
+ }
19
+ }, [
20
+ events.length
21
+ ]);
22
+ const toggleEventExpansion = (index)=>{
23
+ const newExpanded = new Set(expandedEvents);
24
+ if (newExpanded.has(index)) newExpanded.delete(index);
25
+ else newExpanded.add(index);
26
+ setExpandedEvents(newExpanded);
27
+ };
28
+ const truncateJsonStrings = (obj, maxLength = 30)=>{
29
+ if ('string' == typeof obj) return obj.length > maxLength ? `${obj.substring(0, maxLength)}...` : obj;
30
+ if (Array.isArray(obj)) return obj.map((item)=>truncateJsonStrings(item, maxLength));
31
+ if (obj && 'object' == typeof obj) {
32
+ const truncated = {};
33
+ for(const key in obj)if (Object.prototype.hasOwnProperty.call(obj, key)) truncated[key] = truncateJsonStrings(obj[key], maxLength);
34
+ return truncated;
35
+ }
36
+ return obj;
37
+ };
38
+ const copyToClipboard = (text)=>{
39
+ navigator.clipboard.writeText(text).then(()=>{
40
+ message.success('JSON copied to clipboard');
41
+ }).catch(()=>{
42
+ message.error('Copy failed');
43
+ });
44
+ };
45
+ const getEventIcon = (type)=>{
46
+ switch(type){
47
+ case 'click':
48
+ return /*#__PURE__*/ jsx(AimOutlined, {
49
+ style: {
50
+ color: '#1890ff'
51
+ }
52
+ });
53
+ case 'input':
54
+ return /*#__PURE__*/ jsx(EditOutlined, {
55
+ style: {
56
+ color: '#52c41a'
57
+ }
58
+ });
59
+ case 'scroll':
60
+ return /*#__PURE__*/ jsx(VerticalAlignTopOutlined, {
61
+ style: {
62
+ color: '#faad14'
63
+ }
64
+ });
65
+ case 'navigation':
66
+ return /*#__PURE__*/ jsx(CompassOutlined, {
67
+ style: {
68
+ color: '#722ed1'
69
+ }
70
+ });
71
+ case 'setViewport':
72
+ return /*#__PURE__*/ jsx(CompassOutlined, {
73
+ style: {
74
+ color: '#eb2f96'
75
+ }
76
+ });
77
+ case 'keydown':
78
+ return /*#__PURE__*/ jsx(KeyOutlined, {
79
+ style: {
80
+ color: '#fa8c16'
81
+ }
82
+ });
83
+ default:
84
+ return /*#__PURE__*/ jsx(AimOutlined, {
85
+ style: {
86
+ color: '#d9d9d9'
87
+ }
88
+ });
89
+ }
90
+ };
91
+ const getEventColor = (type)=>{
92
+ switch(type){
93
+ case 'click':
94
+ return '#1890ff';
95
+ case 'input':
96
+ return '#52c41a';
97
+ case 'scroll':
98
+ return '#faad14';
99
+ case 'navigation':
100
+ return '#722ed1';
101
+ case 'setViewport':
102
+ return '#eb2f96';
103
+ case 'keydown':
104
+ return '#fa8c16';
105
+ default:
106
+ return '#d9d9d9';
107
+ }
108
+ };
109
+ const getEventTitle = (event)=>{
110
+ switch(event.type){
111
+ case 'click':
112
+ if ('BUTTON' === event.targetTagName) return 'Click Button';
113
+ if (event.value) return `Click Element "${event.value}"`;
114
+ return 'Click';
115
+ case 'input':
116
+ return 'Input';
117
+ case 'scroll':
118
+ return 'Scroll';
119
+ case 'navigation':
120
+ return 'Navigate';
121
+ case 'setViewport':
122
+ return 'Set viewport';
123
+ case 'keydown':
124
+ return 'Key down';
125
+ default:
126
+ return event.type;
127
+ }
128
+ };
129
+ const getEventDescription = (event)=>{
130
+ const eventTitle = getEventTitle(event);
131
+ switch(event.type){
132
+ case 'click':
133
+ if (true === event.descriptionLoading && event.elementRect?.x !== void 0 && event.elementRect?.y !== void 0) return /*#__PURE__*/ jsxs("span", {
134
+ style: {
135
+ display: 'flex',
136
+ alignItems: 'center',
137
+ gap: '4px'
138
+ },
139
+ children: [
140
+ /*#__PURE__*/ jsxs(Text, {
141
+ children: [
142
+ eventTitle,
143
+ " - "
144
+ ]
145
+ }),
146
+ /*#__PURE__*/ jsx(ShinyText, {
147
+ text: `(${event.elementRect.x}, ${event.elementRect.y})`,
148
+ disabled: false,
149
+ speed: 3,
150
+ className: "step-title-shiny"
151
+ })
152
+ ]
153
+ });
154
+ if (false === event.descriptionLoading && event.elementDescription) return /*#__PURE__*/ jsxs(Text, {
155
+ className: "",
156
+ children: [
157
+ eventTitle,
158
+ " - ",
159
+ event.elementDescription
160
+ ]
161
+ });
162
+ return /*#__PURE__*/ jsx(Text, {
163
+ children: eventTitle
164
+ });
165
+ case 'input':
166
+ if (false === event.descriptionLoading && event.elementDescription) return /*#__PURE__*/ jsxs(Text, {
167
+ children: [
168
+ eventTitle,
169
+ " - ",
170
+ event.elementDescription
171
+ ]
172
+ });
173
+ return /*#__PURE__*/ jsxs("span", {
174
+ style: {
175
+ display: 'flex',
176
+ alignItems: 'center',
177
+ gap: '4px'
178
+ },
179
+ children: [
180
+ /*#__PURE__*/ jsxs(Text, {
181
+ children: [
182
+ eventTitle,
183
+ " - "
184
+ ]
185
+ }),
186
+ /*#__PURE__*/ jsx(ShinyText, {
187
+ text: event.value ? `"${event.value}"` : '',
188
+ disabled: false,
189
+ speed: 3,
190
+ className: "step-title-shiny"
191
+ })
192
+ ]
193
+ });
194
+ case 'scroll':
195
+ if (event.elementDescription) return /*#__PURE__*/ jsxs(Text, {
196
+ children: [
197
+ eventTitle,
198
+ " - ",
199
+ event.value?.split(' ')[0] || ''
200
+ ]
201
+ });
202
+ return /*#__PURE__*/ jsxs(Text, {
203
+ children: [
204
+ eventTitle,
205
+ " - Position: (",
206
+ event.elementRect?.x || 0,
207
+ ",",
208
+ ' ',
209
+ event.elementRect?.y || 0,
210
+ ")"
211
+ ]
212
+ });
213
+ case 'navigation':
214
+ {
215
+ const truncatedUrl = event.url && event.url.length > 50 ? `${event.url.substring(0, 50)}...` : event.url;
216
+ return /*#__PURE__*/ jsxs(Text, {
217
+ children: [
218
+ eventTitle,
219
+ " - ",
220
+ truncatedUrl
221
+ ]
222
+ });
223
+ }
224
+ case 'setViewport':
225
+ return /*#__PURE__*/ jsxs(Text, {
226
+ children: [
227
+ eventTitle,
228
+ " - Desktop 964x992 px"
229
+ ]
230
+ });
231
+ case 'keydown':
232
+ return /*#__PURE__*/ jsxs(Text, {
233
+ children: [
234
+ eventTitle,
235
+ " - Key: ",
236
+ event.value || 'Unknown'
237
+ ]
238
+ });
239
+ default:
240
+ return /*#__PURE__*/ jsx(Text, {
241
+ children: eventTitle
242
+ });
243
+ }
244
+ };
245
+ const timelineItems = events.map((event, index)=>{
246
+ const boxedImage = event.screenshotWithBox;
247
+ const afterImage = event.screenshotAfter;
248
+ const isExpanded = expandedEvents.has(index);
249
+ return {
250
+ dot: getEventIcon(event.type),
251
+ color: getEventColor(event.type),
252
+ children: /*#__PURE__*/ jsx("div", {
253
+ children: /*#__PURE__*/ jsxs(Card, {
254
+ size: "small",
255
+ bordered: false,
256
+ style: {
257
+ marginBottom: isExpanded ? 8 : 8,
258
+ cursor: 'pointer'
259
+ },
260
+ onClick: ()=>{
261
+ toggleEventExpansion(index);
262
+ onEventClick?.(event, index);
263
+ },
264
+ styles: {
265
+ body: {
266
+ padding: '8px 12px',
267
+ backgroundColor: '#F2F4F7',
268
+ borderRadius: '8px'
269
+ }
270
+ },
271
+ children: [
272
+ /*#__PURE__*/ jsxs(Space, {
273
+ style: {
274
+ width: '100%',
275
+ justifyContent: 'space-between',
276
+ alignItems: 'center',
277
+ color: 'rgba(0, 0, 0, 0.85)'
278
+ },
279
+ children: [
280
+ /*#__PURE__*/ jsx(Space, {
281
+ style: {
282
+ flex: 1,
283
+ minWidth: 0
284
+ },
285
+ children: getEventDescription(event)
286
+ }),
287
+ /*#__PURE__*/ jsx(Space, {
288
+ children: (boxedImage || afterImage) && /*#__PURE__*/ jsxs("div", {
289
+ style: {
290
+ display: 'flex',
291
+ alignItems: 'center'
292
+ },
293
+ children: [
294
+ boxedImage && /*#__PURE__*/ jsx("div", {
295
+ style: {
296
+ width: '24px',
297
+ height: '24px',
298
+ borderRadius: '4px',
299
+ overflow: 'hidden',
300
+ boxShadow: '1px 1px 1px 1px #00000014',
301
+ cursor: 'pointer',
302
+ transition: 'all 0.2s ease-in-out',
303
+ zIndex: 2
304
+ },
305
+ onMouseEnter: (e)=>{
306
+ const target = e.currentTarget;
307
+ target.style.transform = 'scale(1.2)';
308
+ target.style.boxShadow = `0 2px 8px ${getEventColor(event.type)}60`;
309
+ },
310
+ onMouseLeave: (e)=>{
311
+ const target = e.currentTarget;
312
+ target.style.transform = 'scale(1)';
313
+ target.style.boxShadow = '1px 1px 1px 1px #00000014';
314
+ },
315
+ onClick: (e)=>{
316
+ e.stopPropagation();
317
+ },
318
+ children: /*#__PURE__*/ jsx(Image, {
319
+ src: boxedImage,
320
+ width: "100%",
321
+ height: "100%",
322
+ style: {
323
+ objectFit: 'cover',
324
+ display: 'block'
325
+ },
326
+ preview: {
327
+ mask: false
328
+ }
329
+ })
330
+ }),
331
+ afterImage && /*#__PURE__*/ jsx("div", {
332
+ style: {
333
+ width: '24px',
334
+ height: '24px',
335
+ borderRadius: '4px',
336
+ overflow: 'hidden',
337
+ boxShadow: '1px 1px 1px 1px #00000014',
338
+ cursor: 'pointer',
339
+ transition: 'all 0.2s ease-in-out',
340
+ marginLeft: boxedImage ? '-8px' : '0',
341
+ zIndex: 1
342
+ },
343
+ onMouseEnter: (e)=>{
344
+ const target = e.currentTarget;
345
+ target.style.transform = 'scale(1.2)';
346
+ target.style.boxShadow = '0 2px 8px #52c41a60';
347
+ },
348
+ onMouseLeave: (e)=>{
349
+ const target = e.currentTarget;
350
+ target.style.transform = 'scale(1)';
351
+ target.style.boxShadow = '1px 1px 1px 1px #00000014';
352
+ },
353
+ onClick: (e)=>{
354
+ e.stopPropagation();
355
+ },
356
+ children: /*#__PURE__*/ jsx(Image, {
357
+ src: afterImage,
358
+ width: "100%",
359
+ height: "100%",
360
+ style: {
361
+ objectFit: 'cover',
362
+ display: 'block'
363
+ },
364
+ preview: {
365
+ mask: false
366
+ }
367
+ })
368
+ })
369
+ ]
370
+ })
371
+ })
372
+ ]
373
+ }),
374
+ isExpanded && /*#__PURE__*/ jsx("div", {
375
+ style: {
376
+ marginTop: 8,
377
+ marginBottom: 8
378
+ },
379
+ children: /*#__PURE__*/ jsx(Card, {
380
+ size: "small",
381
+ style: {
382
+ backgroundColor: '#f5f5f5'
383
+ },
384
+ bodyStyle: {
385
+ padding: '0px'
386
+ },
387
+ children: /*#__PURE__*/ jsxs("div", {
388
+ style: {
389
+ position: 'relative'
390
+ },
391
+ children: [
392
+ /*#__PURE__*/ jsx("pre", {
393
+ style: {
394
+ fontSize: '12px',
395
+ margin: 0,
396
+ whiteSpace: 'pre-wrap',
397
+ backgroundColor: '#ffffff',
398
+ padding: '12px',
399
+ borderRadius: '8px',
400
+ maxHeight: '250px',
401
+ overflow: 'auto'
402
+ },
403
+ children: JSON.stringify(truncateJsonStrings(event), null, 2)
404
+ }),
405
+ /*#__PURE__*/ jsx(Button, {
406
+ type: "text",
407
+ size: "small",
408
+ icon: /*#__PURE__*/ jsx(CopyOutlined, {}),
409
+ onClick: (e)=>{
410
+ e.stopPropagation();
411
+ copyToClipboard(JSON.stringify(event, null, 2));
412
+ },
413
+ style: {
414
+ position: 'absolute',
415
+ top: '8px',
416
+ right: '8px',
417
+ background: 'rgba(255, 255, 255, 0.9)',
418
+ border: '1px solid #d9d9d9'
419
+ },
420
+ title: "Copy JSON"
421
+ })
422
+ ]
423
+ })
424
+ })
425
+ })
426
+ ]
427
+ })
428
+ })
429
+ };
430
+ });
431
+ return /*#__PURE__*/ jsx("div", {
432
+ style: {
433
+ padding: '3px'
434
+ },
435
+ children: /*#__PURE__*/ jsx(Space, {
436
+ direction: "vertical",
437
+ size: "large",
438
+ style: {
439
+ width: '100%'
440
+ },
441
+ children: /*#__PURE__*/ jsx(Timeline, {
442
+ mode: "left",
443
+ className: "timeline-scrollable",
444
+ items: timelineItems,
445
+ style: {
446
+ paddingTop: 16
447
+ }
448
+ })
449
+ })
450
+ });
451
+ };
452
+ export { RecordTimeline };
@@ -0,0 +1,35 @@
1
+ .demo-button {
2
+ cursor: pointer;
3
+ border: 0;
4
+ border-radius: 3em;
5
+ font-weight: 700;
6
+ line-height: 1;
7
+ display: inline-block;
8
+ }
9
+
10
+ .demo-button--primary {
11
+ color: #fff;
12
+ background-color: #1ea7fd;
13
+ }
14
+
15
+ .demo-button--secondary {
16
+ color: #333;
17
+ background-color: #0000;
18
+ box-shadow: inset 0 0 0 1px #00000026;
19
+ }
20
+
21
+ .demo-button--small {
22
+ padding: 10px 16px;
23
+ font-size: 12px;
24
+ }
25
+
26
+ .demo-button--medium {
27
+ padding: 11px 20px;
28
+ font-size: 14px;
29
+ }
30
+
31
+ .demo-button--large {
32
+ padding: 12px 24px;
33
+ font-size: 16px;
34
+ }
35
+
@@ -0,0 +1,73 @@
1
+ .shiny-text {
2
+ color: #0000;
3
+ letter-spacing: .5px;
4
+ text-shadow: 0 1px 2px #0000000d;
5
+ background-image: linear-gradient(45deg, #2b83ff, #6a11cb, #2575fc, #4481eb);
6
+ background-size: 300%;
7
+ background-clip: text;
8
+ font-weight: 600;
9
+ animation: 8s infinite textGradient;
10
+ display: inline-block;
11
+ position: relative;
12
+ overflow: hidden;
13
+ }
14
+
15
+ .shiny-text:after {
16
+ content: "";
17
+ width: 120%;
18
+ height: 120%;
19
+ animation: shine var(--animation-duration, 5s) cubic-bezier(.25, .1, .25, 1) infinite;
20
+ z-index: 1;
21
+ pointer-events: none;
22
+ background: linear-gradient(90deg, #fff0 0%, #ffffff1a 10%, #fff9 50%, #ffffff1a 90%, #fff0 100%);
23
+ position: absolute;
24
+ top: -10%;
25
+ left: -150%;
26
+ transform: skewX(-20deg)translateY(0);
27
+ }
28
+
29
+ .shiny-text.disabled {
30
+ background: #2b83ff;
31
+ background-clip: text;
32
+ animation: none;
33
+ }
34
+
35
+ .shiny-text.disabled:after {
36
+ animation: none;
37
+ display: none;
38
+ }
39
+
40
+ @keyframes shine {
41
+ 0% {
42
+ opacity: .7;
43
+ left: -150%;
44
+ }
45
+
46
+ 20% {
47
+ opacity: 1;
48
+ }
49
+
50
+ 80% {
51
+ opacity: 1;
52
+ }
53
+
54
+ 100% {
55
+ opacity: .7;
56
+ left: 250%;
57
+ }
58
+ }
59
+
60
+ @keyframes textGradient {
61
+ 0% {
62
+ background-position: 0%;
63
+ }
64
+
65
+ 50% {
66
+ background-position: 100%;
67
+ }
68
+
69
+ 100% {
70
+ background-position: 0%;
71
+ }
72
+ }
73
+
@@ -0,0 +1,13 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import "./shiny-text.css";
3
+ const ShinyText = ({ text, disabled = false, speed = 5, className = '' })=>{
4
+ const style = {
5
+ '--animation-duration': `${speed}s`
6
+ };
7
+ return /*#__PURE__*/ jsx("div", {
8
+ className: `shiny-text ${disabled ? 'disabled' : ''} ${className}`,
9
+ style: style,
10
+ children: text
11
+ });
12
+ };
13
+ export { ShinyText };
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { Button } from "./Button.js";
2
+ import { EventRecorder, convertToChromeEvents } from "./recorder.js";
3
+ import { RecordTimeline } from "./RecordTimeline.js";
4
+ export { Button, EventRecorder, RecordTimeline, convertToChromeEvents };
@@ -0,0 +1,2 @@
1
+ import { EventRecorder } from "./recorder.js";
2
+ window.EventRecorder = EventRecorder;