@@ -10,6 +10,39 @@ type RrwebPlugin = NonNullable<recordOptions<RecordingEvent>['plugins']>[number]
1010
1111type ClickModifiers = Pick < MouseEvent , 'button' | 'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey' > ;
1212
13+ type ClipboardAction = 'copy' | 'cut' | 'paste' ;
14+
15+ /**
16+ * Record clipboard actions and their DOM target without reading clipboard
17+ * contents. The resulting rrweb Plugin events can explain otherwise
18+ * surprising input changes during analysis.
19+ */
20+ function clipboardActionsPlugin ( ) : RrwebPlugin {
21+ let getId : ( ( node : Node ) => number ) | undefined ;
22+
23+ return {
24+ name : 'csr/clipboard@1' ,
25+ options : { } ,
26+ getMirror : ( { nodeMirror } ) => {
27+ getId = node => nodeMirror . getId ( node ) ;
28+ } ,
29+ observer : ( callback , win ) => {
30+ const actions : ClipboardAction [ ] = [ 'copy' , 'cut' , 'paste' ] ;
31+ const handlers = actions . map ( action => {
32+ const handler = ( event : Event ) => {
33+ const targetId = event . target instanceof win . Node ? getId ?.( event . target ) ?? - 1 : - 1 ;
34+ callback ( { action, targetId } ) ;
35+ } ;
36+
37+ win . document . addEventListener ( action , handler , true ) ;
38+ return ( ) => win . document . removeEventListener ( action , handler , true ) ;
39+ } ) ;
40+
41+ return ( ) => handlers . forEach ( remove => remove ( ) ) ;
42+ } ,
43+ } ;
44+ }
45+
1346/**
1447 * rrweb does not include modifier keys in mouse-interaction events. Capture
1548 * the native click first, then add its safe, non-text metadata to the rrweb
@@ -69,7 +102,7 @@ export class RrwebEngine implements RecordingEngine {
69102 const maskSelectors = config . maskSelectors ?? DEFAULT_MASK_SELECTORS ;
70103 const blockSelectors = config . blockSelectors ?? DEFAULT_BLOCK_SELECTORS ;
71104
72- const plugins : RrwebPlugin [ ] = [ clickModifiersPlugin ( ) ] ;
105+ const plugins : RrwebPlugin [ ] = [ clickModifiersPlugin ( ) , clipboardActionsPlugin ( ) ] ;
73106 const { captureConsoleLogs } = config ;
74107 if ( captureConsoleLogs ) {
75108 const levels = captureConsoleLogs === true ? ALL_CONSOLE_LEVELS : captureConsoleLogs . levels ;
0 commit comments