@scalebun/web 1.4.0 → 1.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/dist/core/version.generated.d.ts +1 -1
- package/dist/core/version.generated.js +1 -1
- package/dist/features/journey/InteractionTracker.d.ts +31 -1
- package/dist/features/journey/InteractionTracker.d.ts.map +1 -1
- package/dist/features/journey/InteractionTracker.js +117 -34
- package/dist/features/journey/InteractionTracker.js.map +1 -1
- package/dist/features/journey/JourneyFeature.d.ts +1 -1
- package/dist/features/journey/JourneyFeature.d.ts.map +1 -1
- package/dist/features/journey/JourneyFeature.js +6 -4
- package/dist/features/journey/JourneyFeature.js.map +1 -1
- package/dist/features/journey/friction.d.ts +3 -4
- package/dist/features/journey/friction.d.ts.map +1 -1
- package/dist/features/journey/friction.js +7 -8
- package/dist/features/journey/friction.js.map +1 -1
- package/dist/features/journey/uiState.d.ts.map +1 -1
- package/dist/features/journey/uiState.js +10 -4
- package/dist/features/journey/uiState.js.map +1 -1
- package/dist/scalebun.min.js +17 -17
- package/dist/scalebun.min.js.map +3 -3
- package/package.json +3 -3
|
@@ -6,7 +6,11 @@ export interface InteractionTrackerOptions {
|
|
|
6
6
|
/** Mask the label text (`privacy.maskEventText`). Default false — the real label is sent. */
|
|
7
7
|
maskText?: boolean;
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
export interface InteractionEmission {
|
|
10
|
+
interactionId: string;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
type EmitAction = (name: string, data: Record<string, unknown>, label?: string, interaction?: InteractionEmission) => void;
|
|
10
14
|
export declare class InteractionTracker {
|
|
11
15
|
private readonly emit;
|
|
12
16
|
/** Raise a capture signal ('rage'|'dead') onto the bus — arms friction-triggered features
|
|
@@ -16,12 +20,38 @@ export declare class InteractionTracker {
|
|
|
16
20
|
private readonly dead;
|
|
17
21
|
private readonly maskText;
|
|
18
22
|
private onClickFn?;
|
|
23
|
+
private onPointerDownFn?;
|
|
24
|
+
private onPageHideFn?;
|
|
25
|
+
private pointerDown?;
|
|
26
|
+
private pending;
|
|
19
27
|
constructor(emit: EmitAction, opts?: InteractionTrackerOptions,
|
|
20
28
|
/** Raise a capture signal ('rage'|'dead') onto the bus — arms friction-triggered features
|
|
21
29
|
* (replay capture-on-signal, struggle-triggered surveys/in-app). Optional (tests omit it). */
|
|
22
30
|
signal?: ((reason: string) => void) | undefined);
|
|
23
31
|
start(): void;
|
|
24
32
|
stop(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Emit the one row for this interaction.
|
|
35
|
+
*
|
|
36
|
+
* `dead` is THREE-VALUED here, and that is the point. `true` and `false` are verdicts the watcher
|
|
37
|
+
* actually reached; `undefined` means the page went away before the observation window closed, so
|
|
38
|
+
* nobody looked. Writing `dead: false` in that case asserts a measurement never taken — "this click
|
|
39
|
+
* DID cause a change" — and a dead-click rate computed from it is wrong in exactly the situation
|
|
40
|
+
* where dead clicks matter most: the visitor left immediately after clicking.
|
|
41
|
+
*
|
|
42
|
+
* So the key is omitted rather than defaulted, the same rule the rest of this pipeline follows:
|
|
43
|
+
* absent means unknown, never zero. `deadUnknown` records WHY it is absent, so a consumer can tell
|
|
44
|
+
* "not dead" from "we never found out".
|
|
45
|
+
*/
|
|
46
|
+
private finish;
|
|
47
|
+
/**
|
|
48
|
+
* Flush every interaction still awaiting its dead-click verdict.
|
|
49
|
+
*
|
|
50
|
+
* Runs on `pagehide` and on `stop()`. Without it a click followed immediately by a navigation is
|
|
51
|
+
* never emitted at all — the whole row is lost, which is strictly worse than an unknown flag. The
|
|
52
|
+
* verdict is passed as `undefined` because none was reached; see finish().
|
|
53
|
+
*/
|
|
54
|
+
private flushPending;
|
|
25
55
|
}
|
|
26
56
|
/**
|
|
27
57
|
* A human-ish label: tag + short text, e.g. `button: Buy now`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InteractionTracker.d.ts","sourceRoot":"","sources":["../../../src/features/journey/InteractionTracker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"InteractionTracker.d.ts","sourceRoot":"","sources":["../../../src/features/journey/InteractionTracker.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,yBAAyB;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,UAAU,GAAG,CAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,CAAC,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,mBAAmB,KAC9B,IAAI,CAAC;AAUV,qBAAa,kBAAkB;IAW3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAErB;mGAC+F;IAC/F,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAd1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,SAAS,CAAC,CAAqB;IACvC,OAAO,CAAC,eAAe,CAAC,CAAqB;IAC7C,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,WAAW,CAAC,CAAgB;IACpC,OAAO,CAAC,OAAO,CAAoC;gBAGhC,IAAI,EAAE,UAAU,EACjC,IAAI,GAAE,yBAA8B;IACpC;mGAC+F;IAC9E,MAAM,CAAC,GAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,aAAA;IAOpD,KAAK,IAAI,IAAI;IAsCb,IAAI,IAAI,IAAI;IAaZ;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,MAAM;IAYd;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;CAGrB;AAqPD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,UAAQ,GAAG,MAAM,CAK9D"}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { RageDetector, DeadClickWatcher } from './friction.js';
|
|
13
13
|
import { getPositionContext } from './positionContext.js';
|
|
14
14
|
import { uiStateSignature } from './uiState.js';
|
|
15
|
+
import { getReplayCoord, uuid } from '../../core/index.js';
|
|
15
16
|
export class InteractionTracker {
|
|
16
17
|
constructor(emit, opts = {},
|
|
17
18
|
/** Raise a capture signal ('rage'|'dead') onto the bus — arms friction-triggered features
|
|
@@ -19,6 +20,7 @@ export class InteractionTracker {
|
|
|
19
20
|
signal) {
|
|
20
21
|
this.emit = emit;
|
|
21
22
|
this.signal = signal;
|
|
23
|
+
this.pending = new Map();
|
|
22
24
|
this.rage = new RageDetector(opts.rageWindowMs ?? 800, opts.rageRadius ?? 30, opts.rageMin ?? 3);
|
|
23
25
|
this.dead = new DeadClickWatcher(opts.deadMs ?? 700);
|
|
24
26
|
this.maskText = opts.maskText ?? false;
|
|
@@ -27,51 +29,135 @@ export class InteractionTracker {
|
|
|
27
29
|
if (typeof document === 'undefined')
|
|
28
30
|
return;
|
|
29
31
|
this.dead.observe(document);
|
|
32
|
+
this.onPointerDownFn = (e) => {
|
|
33
|
+
const pe = e;
|
|
34
|
+
if (pe.isPrimary === false || (typeof pe.button === 'number' && pe.button !== 0))
|
|
35
|
+
return;
|
|
36
|
+
const target = eventTarget(e);
|
|
37
|
+
if (!target)
|
|
38
|
+
return;
|
|
39
|
+
this.pointerDown = capture(target, pe, Date.now(), this.maskText);
|
|
40
|
+
};
|
|
30
41
|
this.onClickFn = (e) => {
|
|
31
42
|
const me = e;
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
// path is what identifies the control. Our own surfaces stay reported as their host, which
|
|
35
|
-
// is what keeps the query-side exclusion matching them.
|
|
36
|
-
let target = e.target;
|
|
37
|
-
if (!target || !target.tagName)
|
|
43
|
+
const target = eventTarget(e);
|
|
44
|
+
if (!target)
|
|
38
45
|
return;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// sizes (heatmaps). Compact keys keep the event small.
|
|
48
|
-
const p = pointer(me);
|
|
49
|
-
// H7 — element anchoring + component identity. The target's bounding rect lets the dashboard re-project
|
|
50
|
-
// a hotspot onto the element at any viewport (responsive), and `component` (opt-in data-scalebun-component,
|
|
51
|
-
// walked up ancestors) groups clicks BY reusable component across every route it appears on.
|
|
52
|
-
const el = anchor(target);
|
|
53
|
-
const rage = this.rage.push(me.clientX, me.clientY, t);
|
|
54
|
-
const data = { action: 'click', ...p, selector, ...el };
|
|
46
|
+
const down = this.pointerDown;
|
|
47
|
+
this.pointerDown = undefined;
|
|
48
|
+
// A real pointer click reuses the evidence captured when the finger/button landed. Keyboard
|
|
49
|
+
// and programmatic clicks have no pointerdown and are measured honestly at click time.
|
|
50
|
+
const captured = me.detail !== 0 && down && down.target === target && Date.now() - down.timestamp < 5000
|
|
51
|
+
? down
|
|
52
|
+
: capture(target, me, Date.now(), this.maskText);
|
|
53
|
+
const rage = this.rage.push(captured.x, captured.y, captured.timestamp);
|
|
55
54
|
if (rage)
|
|
56
|
-
data.rage = true;
|
|
57
|
-
this.emit('USER_ACTION', data, label);
|
|
55
|
+
captured.data.rage = true;
|
|
58
56
|
if (rage)
|
|
59
57
|
this.signal?.('rage'); // RageDetector coalesces → one signal per cluster
|
|
60
|
-
this.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
this.pending.set(captured.interactionId, captured);
|
|
59
|
+
this.dead.onClick((dead) => {
|
|
60
|
+
this.finish(captured.interactionId, dead);
|
|
61
|
+
if (dead)
|
|
62
|
+
this.signal?.('dead');
|
|
65
63
|
});
|
|
66
64
|
};
|
|
65
|
+
this.onPageHideFn = () => this.flushPending();
|
|
66
|
+
document.addEventListener('pointerdown', this.onPointerDownFn, true);
|
|
67
67
|
document.addEventListener('click', this.onClickFn, true);
|
|
68
|
+
if (typeof window !== 'undefined')
|
|
69
|
+
window.addEventListener('pagehide', this.onPageHideFn, true);
|
|
68
70
|
}
|
|
69
71
|
stop() {
|
|
72
|
+
this.flushPending();
|
|
70
73
|
this.dead.disconnect();
|
|
71
|
-
if (
|
|
72
|
-
|
|
74
|
+
if (typeof document !== 'undefined') {
|
|
75
|
+
if (this.onPointerDownFn)
|
|
76
|
+
document.removeEventListener('pointerdown', this.onPointerDownFn, true);
|
|
77
|
+
if (this.onClickFn)
|
|
78
|
+
document.removeEventListener('click', this.onClickFn, true);
|
|
73
79
|
}
|
|
80
|
+
if (this.onPageHideFn && typeof window !== 'undefined') {
|
|
81
|
+
window.removeEventListener('pagehide', this.onPageHideFn, true);
|
|
82
|
+
}
|
|
83
|
+
this.pointerDown = undefined;
|
|
74
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Emit the one row for this interaction.
|
|
87
|
+
*
|
|
88
|
+
* `dead` is THREE-VALUED here, and that is the point. `true` and `false` are verdicts the watcher
|
|
89
|
+
* actually reached; `undefined` means the page went away before the observation window closed, so
|
|
90
|
+
* nobody looked. Writing `dead: false` in that case asserts a measurement never taken — "this click
|
|
91
|
+
* DID cause a change" — and a dead-click rate computed from it is wrong in exactly the situation
|
|
92
|
+
* where dead clicks matter most: the visitor left immediately after clicking.
|
|
93
|
+
*
|
|
94
|
+
* So the key is omitted rather than defaulted, the same rule the rest of this pipeline follows:
|
|
95
|
+
* absent means unknown, never zero. `deadUnknown` records WHY it is absent, so a consumer can tell
|
|
96
|
+
* "not dead" from "we never found out".
|
|
97
|
+
*/
|
|
98
|
+
finish(interactionId, dead) {
|
|
99
|
+
const captured = this.pending.get(interactionId);
|
|
100
|
+
if (!captured)
|
|
101
|
+
return;
|
|
102
|
+
this.pending.delete(interactionId);
|
|
103
|
+
if (dead === true)
|
|
104
|
+
captured.data.dead = true;
|
|
105
|
+
else if (dead === undefined)
|
|
106
|
+
captured.data.deadUnknown = true;
|
|
107
|
+
this.emit('USER_ACTION', captured.data, captured.label, {
|
|
108
|
+
interactionId,
|
|
109
|
+
timestamp: captured.timestamp,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Flush every interaction still awaiting its dead-click verdict.
|
|
114
|
+
*
|
|
115
|
+
* Runs on `pagehide` and on `stop()`. Without it a click followed immediately by a navigation is
|
|
116
|
+
* never emitted at all — the whole row is lost, which is strictly worse than an unknown flag. The
|
|
117
|
+
* verdict is passed as `undefined` because none was reached; see finish().
|
|
118
|
+
*/
|
|
119
|
+
flushPending() {
|
|
120
|
+
for (const interactionId of [...this.pending.keys()])
|
|
121
|
+
this.finish(interactionId, undefined);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function eventTarget(e) {
|
|
125
|
+
// SHADOW DOM — see the note below. `e.target` is retargeted to the host, so the composed
|
|
126
|
+
// path is what identifies the control. Our own surfaces stay reported as their host, which
|
|
127
|
+
// is what keeps the query-side exclusion matching them.
|
|
128
|
+
let target = e.target;
|
|
129
|
+
if (!target || !target.tagName)
|
|
130
|
+
return null;
|
|
131
|
+
if (!target.hasAttribute('data-scalebun')) {
|
|
132
|
+
const deep = e.composedPath?.()[0];
|
|
133
|
+
if (deep && deep.tagName)
|
|
134
|
+
target = deep;
|
|
135
|
+
}
|
|
136
|
+
return target;
|
|
137
|
+
}
|
|
138
|
+
function capture(target, pointerEvent, timestamp, maskText) {
|
|
139
|
+
const ui = uiStateSignature();
|
|
140
|
+
const data = {
|
|
141
|
+
action: 'click',
|
|
142
|
+
...pointer(pointerEvent),
|
|
143
|
+
selector: selectorOf(target),
|
|
144
|
+
...anchor(target),
|
|
145
|
+
state_status: ui ? 'captured_nonempty' : 'captured_empty',
|
|
146
|
+
};
|
|
147
|
+
if (ui)
|
|
148
|
+
data.ui = ui;
|
|
149
|
+
const replay = getReplayCoord();
|
|
150
|
+
if (replay)
|
|
151
|
+
data.replayCoord = replay;
|
|
152
|
+
return {
|
|
153
|
+
target,
|
|
154
|
+
x: pointerEvent.clientX,
|
|
155
|
+
y: pointerEvent.clientY,
|
|
156
|
+
data,
|
|
157
|
+
label: describe(target, maskText),
|
|
158
|
+
interactionId: `ixw-${uuid()}`,
|
|
159
|
+
timestamp,
|
|
160
|
+
};
|
|
75
161
|
}
|
|
76
162
|
/**
|
|
77
163
|
* The element the visitor actually clicked, not the wrapper the browser reports.
|
|
@@ -185,9 +271,6 @@ function anchor(el) {
|
|
|
185
271
|
const cx = constraintOf(el);
|
|
186
272
|
if (cx)
|
|
187
273
|
out.cx = cx;
|
|
188
|
-
const ui = uiStateSignature();
|
|
189
|
-
if (ui)
|
|
190
|
-
out.ui = ui;
|
|
191
274
|
const comp = el.closest('[data-scalebun-component]')?.getAttribute('data-scalebun-component');
|
|
192
275
|
if (comp)
|
|
193
276
|
out.component = comp;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InteractionTracker.js","sourceRoot":"","sources":["../../../src/features/journey/InteractionTracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"InteractionTracker.js","sourceRoot":"","sources":["../../../src/features/journey/InteractionTracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AA+BlD,MAAM,OAAO,kBAAkB;IAU7B,YACmB,IAAgB,EACjC,OAAkC,EAAE;IACpC;mGAC+F;IAC9E,MAAiC;QAJjC,SAAI,GAAJ,IAAI,CAAY;QAIhB,WAAM,GAAN,MAAM,CAA2B;QAP5C,YAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;QASjD,IAAI,CAAC,IAAI,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,YAAY,IAAI,GAAG,EAAE,IAAI,CAAC,UAAU,IAAI,EAAE,EAAE,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;QACjG,IAAI,CAAC,IAAI,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC;QACrD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC;IACzC,CAAC;IAED,KAAK;QACH,IAAI,OAAO,QAAQ,KAAK,WAAW;YAAE,OAAO;QAC5C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5B,IAAI,CAAC,eAAe,GAAG,CAAC,CAAQ,EAAE,EAAE;YAClC,MAAM,EAAE,GAAG,CAAiB,CAAC;YAC7B,IAAI,EAAE,CAAC,SAAS,KAAK,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC;gBAAE,OAAO;YACzF,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,MAAM;gBAAE,OAAO;YACpB,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpE,CAAC,CAAC;QACF,IAAI,CAAC,SAAS,GAAG,CAAC,CAAQ,EAAE,EAAE;YAC5B,MAAM,EAAE,GAAG,CAAe,CAAC;YAC3B,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,MAAM;gBAAE,OAAO;YACpB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;YAC9B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;YAC7B,4FAA4F;YAC5F,uFAAuF;YACvF,MAAM,QAAQ,GACZ,EAAE,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,GAAG,IAAI;gBACrF,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;YACxE,IAAI,IAAI;gBAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;YACpC,IAAI,IAAI;gBAAE,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,kDAAkD;YAEnF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;YACnD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;gBACzB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;gBAC1C,IAAI,IAAI;oBAAE,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;YAClC,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QACF,IAAI,CAAC,YAAY,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;QAC9C,QAAQ,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;QACrE,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACzD,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAClG,CAAC;IAED,IAAI;QACF,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;QACvB,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;YACpC,IAAI,IAAI,CAAC,eAAe;gBAAE,QAAQ,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;YAClG,IAAI,IAAI,CAAC,SAAS;gBAAE,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAClF,CAAC;QACD,IAAI,IAAI,CAAC,YAAY,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YACvD,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAClE,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;IAC/B,CAAC;IAED;;;;;;;;;;;;OAYG;IACK,MAAM,CAAC,aAAqB,EAAE,IAAyB;QAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACjD,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QACnC,IAAI,IAAI,KAAK,IAAI;YAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;aACxC,IAAI,IAAI,KAAK,SAAS;YAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC9D,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,EAAE;YACtD,aAAa;YACb,SAAS,EAAE,QAAQ,CAAC,SAAS;SAC9B,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACK,YAAY;QAClB,KAAK,MAAM,aAAa,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IAC9F,CAAC;CACF;AAED,SAAS,WAAW,CAAC,CAAQ;IAC3B,yFAAyF;IACzF,2FAA2F;IAC3F,wDAAwD;IACxD,IAAI,MAAM,GAAG,CAAC,CAAC,MAAwB,CAAC;IACxC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAwB,CAAC;QAC1D,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO;YAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,OAAO,CAAC,MAAe,EAAE,YAAwB,EAAE,SAAiB,EAAE,QAAiB;IAC9F,MAAM,EAAE,GAAG,gBAAgB,EAAE,CAAC;IAC9B,MAAM,IAAI,GAA4B;QACpC,MAAM,EAAE,OAAO;QACf,GAAG,OAAO,CAAC,YAAY,CAAC;QACxB,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC;QAC5B,GAAG,MAAM,CAAC,MAAM,CAAC;QACjB,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,gBAAgB;KAC1D,CAAC;IACF,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC;IAChC,IAAI,MAAM;QAAE,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;IACtC,OAAO;QACL,MAAM;QACN,CAAC,EAAE,YAAY,CAAC,OAAO;QACvB,CAAC,EAAE,YAAY,CAAC,OAAO;QACvB,IAAI;QACJ,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;QACjC,aAAa,EAAE,OAAO,IAAI,EAAE,EAAE;QAC9B,SAAS;KACV,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,0EAA0E;AAE1E;;;;;;;;;uEASuE;AACvE,SAAS,OAAO,CAAC,EAAc;IAC7B,MAAM,CAAC,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7D,OAAO;QACL,CAAC,EAAE,EAAE,CAAC,OAAO;QACb,CAAC,EAAE,EAAE,CAAC,OAAO;QACb,EAAE,EAAE,CAAC,EAAE,UAAU,IAAI,CAAC;QACtB,EAAE,EAAE,CAAC,EAAE,WAAW,IAAI,CAAC;QACvB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACjC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACjC,EAAE,EAAE,QAAQ,CAAC,eAAe,CAAC,YAAY;QACzC,gGAAgG;QAChG,GAAG,EAAE,CAAC,EAAE,gBAAgB,IAAI,CAAC;KAC9B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAS,YAAY,CAAC,EAAW;IAC/B,gGAAgG;IAChG,8EAA8E;IAC9E,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACjE,+FAA+F;IAC/F,iEAAiE;IACjE,IAAI,CAAC;QAAE,OAAO,GAAG,GAAG,CAAC,CAAC;IACtB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,yFAAyF;IACzF,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,GAAG,GAAG,CAAC,CAAC;IAC9D,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAClC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACjC,CAAC;AAED,iHAAiH;AACjH,SAAS,MAAM,CAAC,EAAW;IACzB,MAAM,CAAC,GAAG,EAAE,CAAC,qBAAqB,EAAE,CAAC;IACrC,MAAM,GAAG,GAA4B,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;IACrG;;;;;;;;;;;;;;OAcG;IACH,MAAM,EAAE,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAAC;IAClC,uFAAuF;IACvF,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;IACpB,GAAG,CAAC,IAAI,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;IAC9B,MAAM,EAAE,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;IAC5B,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC;IACpB,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,2BAA2B,CAAC,EAAE,YAAY,CAAC,yBAAyB,CAAC,CAAC;IAC9F,IAAI,IAAI;QAAE,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;IAC/B,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,SAAS,cAAc,CAAC,EAAW;IACjC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,CAAC,GAAmB,EAAE,CAAC;IAC3B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QAC5C,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QACpC,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,MAAM;YAAE,MAAM;QAC5C,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC;YACT,KAAK,CAAC,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAChC,MAAM;QACR,CAAC;QACD,MAAM,EAAE,GAAmB,CAAC,CAAC,aAAa,CAAC;QAC3C,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,MAAM,IAAI,GAAmB,EAAE,CAAC,QAAQ,CAAC;YACzC,MAAM,IAAI,GAAY,CAAC,CAAC;YACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;gBACpB,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO;oBAAE,SAAS;gBACnD,KAAK,EAAE,CAAC;gBACR,IAAI,GAAG,KAAK,IAAI;oBAAE,KAAK,GAAG,KAAK,CAAC;YAClC,CAAC;YACD,KAAK,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,gBAAgB,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAClE,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;QACD,CAAC,GAAG,EAAE,CAAC;IACT,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAW,EAAE,QAAQ,GAAG,KAAK;IACpD,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACxD,IAAI,CAAC,IAAI;QAAE,OAAO,GAAG,CAAC;IACtB,OAAO,GAAG,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACnD,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,UAAU,CAAC,EAAW;IAC7B,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,EAAE,CAAC,EAAE;QAAE,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;SACzC,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,OAAO,CAAC;SACf,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACf,IAAI,GAAG,CAAC,MAAM;QAAE,CAAC,IAAI,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzC,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* F-JRN — journey + navigation. Emits SCREEN_VIEW (route changes) and USER_ACTION (clicks
|
|
2
|
+
* F-JRN — journey + navigation. Emits SCREEN_VIEW (route changes) and USER_ACTION (clicks enriched with rage/dead
|
|
3
3
|
* friction) as `kind:'event'` items → the existing funnels / flows / friction analytics. Always-on,
|
|
4
4
|
* decoupled from replay.
|
|
5
5
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JourneyFeature.d.ts","sourceRoot":"","sources":["../../../src/features/journey/JourneyFeature.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAMxE,qBAAa,cAAe,YAAW,QAAQ;IAC7C,QAAQ,CAAC,IAAI,aAAa;IAC1B,OAAO,CAAC,GAAG,CAA+B;IAC1C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,GAAG,CAAC,CAAoB;IAChC,OAAO,CAAC,YAAY,CAAC,CAAqB;IAE1C,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,KAAK,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"JourneyFeature.d.ts","sourceRoot":"","sources":["../../../src/features/journey/JourneyFeature.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAMxE,qBAAa,cAAe,YAAW,QAAQ;IAC7C,QAAQ,CAAC,IAAI,aAAa;IAC1B,OAAO,CAAC,GAAG,CAA+B;IAC1C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,GAAG,CAAC,CAAoB;IAChC,OAAO,CAAC,YAAY,CAAC,CAAqB;IAE1C,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,KAAK,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI;IAoBhC,OAAO,CAAC,IAAI;IA0BZ,QAAQ,IAAI,IAAI;CAKjB"}
|
|
@@ -15,7 +15,7 @@ export class JourneyFeature {
|
|
|
15
15
|
if (typeof window === 'undefined' || typeof document === 'undefined')
|
|
16
16
|
return;
|
|
17
17
|
this.nav = new NavigationTracker((from, to) => this.emit('SCREEN_VIEW', { from, to }, undefined, to));
|
|
18
|
-
this.interactions = new InteractionTracker((name, data, label) => this.emit(name, data, label, currentPath()),
|
|
18
|
+
this.interactions = new InteractionTracker((name, data, label, interaction) => this.emit(name, data, label, currentPath(), interaction),
|
|
19
19
|
// Label masking is opt-IN (`privacy.maskEventText`). Off by default so USER_ACTION rows carry the
|
|
20
20
|
// real element text and group per element instead of collapsing by text shape.
|
|
21
21
|
{ maskText: ctx.maskEventText === true }, (reason) => this.ctx?.signal(reason));
|
|
@@ -23,12 +23,12 @@ export class JourneyFeature {
|
|
|
23
23
|
this.interactions.start();
|
|
24
24
|
this.active = true;
|
|
25
25
|
}
|
|
26
|
-
emit(name, props, label, screenName) {
|
|
26
|
+
emit(name, props, label, screenName, interaction) {
|
|
27
27
|
// Heatmaps 2.0 (H5) — stamp WHERE the replay was when this happened, so a heatmap hotspot (or friction
|
|
28
28
|
// click) deep-links to the EXACT replay frame, not just the session. Cheap (a getter + spread); only
|
|
29
29
|
// present when replay is producing frames (undefined otherwise). Closes the journey↔replay gap that
|
|
30
30
|
// left the richest heatmap signal (clicks + rage/dead) without a frame coordinate.
|
|
31
|
-
const replay = getReplayCoord();
|
|
31
|
+
const replay = props.replayCoord ? undefined : getReplayCoord();
|
|
32
32
|
const item = {
|
|
33
33
|
kind: 'event',
|
|
34
34
|
name,
|
|
@@ -36,7 +36,9 @@ export class JourneyFeature {
|
|
|
36
36
|
label,
|
|
37
37
|
screenName: screenName ?? currentPath(),
|
|
38
38
|
eventId: `evt_${uuid()}`,
|
|
39
|
-
timestamp: now(),
|
|
39
|
+
timestamp: interaction?.timestamp ?? now(),
|
|
40
|
+
interactionId: interaction?.interactionId,
|
|
41
|
+
interactionProtocol: interaction ? 1 : undefined,
|
|
40
42
|
};
|
|
41
43
|
this.ctx?.emit(item);
|
|
42
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JourneyFeature.js","sourceRoot":"","sources":["../../../src/features/journey/JourneyFeature.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,OAAO,cAAc;IAA3B;QACW,SAAI,GAAG,SAAS,CAAC;QAClB,QAAG,GAA0B,IAAI,CAAC;QAClC,WAAM,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"JourneyFeature.js","sourceRoot":"","sources":["../../../src/features/journey/JourneyFeature.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,14 +16,13 @@ export declare class RageDetector {
|
|
|
16
16
|
/** Fires a callback when a click had NO DOM change and NO navigation within `deadMs`. */
|
|
17
17
|
export declare class DeadClickWatcher {
|
|
18
18
|
private readonly deadMs;
|
|
19
|
-
private
|
|
19
|
+
private changeVersion;
|
|
20
20
|
private observer;
|
|
21
21
|
private timers;
|
|
22
|
-
private startHref;
|
|
23
22
|
constructor(deadMs?: number);
|
|
24
23
|
observe(target: Node): void;
|
|
25
|
-
/**
|
|
26
|
-
onClick(
|
|
24
|
+
/** Resolve exactly once after the observation window. `true` means no DOM change + no navigation. */
|
|
25
|
+
onClick(onResult: (dead: boolean) => void): void;
|
|
27
26
|
disconnect(): void;
|
|
28
27
|
}
|
|
29
28
|
//# sourceMappingURL=friction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"friction.d.ts","sourceRoot":"","sources":["../../../src/features/journey/friction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yGAAyG;AACzG,qBAAa,YAAY;IAGrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAJtB,OAAO,CAAC,OAAO,CAAkD;gBAE9C,QAAQ,SAAM,EACd,MAAM,SAAK,EACX,GAAG,SAAI;IAG1B,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;CAM/C;AAED,yFAAyF;AACzF,qBAAa,gBAAgB;
|
|
1
|
+
{"version":3,"file":"friction.d.ts","sourceRoot":"","sources":["../../../src/features/journey/friction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yGAAyG;AACzG,qBAAa,YAAY;IAGrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAJtB,OAAO,CAAC,OAAO,CAAkD;gBAE9C,QAAQ,SAAM,EACd,MAAM,SAAK,EACX,GAAG,SAAI;IAG1B,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;CAM/C;AAED,yFAAyF;AACzF,qBAAa,gBAAgB;IAKf,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,MAAM,CAA4C;gBAE7B,MAAM,SAAM;IAEzC,OAAO,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI;IAQ3B,qGAAqG;IACrG,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI;IAUhD,UAAU,IAAI,IAAI;CAMnB"}
|
|
@@ -23,26 +23,25 @@ export class RageDetector {
|
|
|
23
23
|
export class DeadClickWatcher {
|
|
24
24
|
constructor(deadMs = 700) {
|
|
25
25
|
this.deadMs = deadMs;
|
|
26
|
-
this.
|
|
26
|
+
this.changeVersion = 0;
|
|
27
27
|
this.observer = null;
|
|
28
28
|
this.timers = new Set();
|
|
29
|
-
this.startHref = '';
|
|
30
29
|
}
|
|
31
30
|
observe(target) {
|
|
32
31
|
if (typeof MutationObserver === 'undefined')
|
|
33
32
|
return;
|
|
34
|
-
this.startHref = href();
|
|
35
33
|
this.observer = new MutationObserver(() => {
|
|
36
|
-
this.
|
|
34
|
+
this.changeVersion++;
|
|
37
35
|
});
|
|
38
36
|
this.observer.observe(target, { subtree: true, childList: true, attributes: true, characterData: true });
|
|
39
37
|
}
|
|
40
|
-
/**
|
|
41
|
-
onClick(
|
|
38
|
+
/** Resolve exactly once after the observation window. `true` means no DOM change + no navigation. */
|
|
39
|
+
onClick(onResult) {
|
|
40
|
+
const version = this.changeVersion;
|
|
41
|
+
const startHref = href();
|
|
42
42
|
const timer = setTimeout(() => {
|
|
43
43
|
this.timers.delete(timer);
|
|
44
|
-
|
|
45
|
-
onDead();
|
|
44
|
+
onResult(this.changeVersion === version && href() === startHref);
|
|
46
45
|
}, this.deadMs);
|
|
47
46
|
this.timers.add(timer);
|
|
48
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"friction.js","sourceRoot":"","sources":["../../../src/features/journey/friction.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,yGAAyG;AACzG,MAAM,OAAO,YAAY;IAEvB,YACmB,WAAW,GAAG,EACd,SAAS,EAAE,EACX,MAAM,CAAC;QAFP,aAAQ,GAAR,QAAQ,CAAM;QACd,WAAM,GAAN,MAAM,CAAK;QACX,QAAG,GAAH,GAAG,CAAI;QAJlB,YAAO,GAA+C,EAAE,CAAC;IAK9D,CAAC;IAEJ,IAAI,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS;QAClC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;QACrF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC;IACrC,CAAC;CACF;AAED,yFAAyF;AACzF,MAAM,OAAO,gBAAgB;
|
|
1
|
+
{"version":3,"file":"friction.js","sourceRoot":"","sources":["../../../src/features/journey/friction.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uiState.d.ts","sourceRoot":"","sources":["../../../src/features/journey/uiState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAaH,6DAA6D;AAC7D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAI5D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAGhD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"uiState.d.ts","sourceRoot":"","sources":["../../../src/features/journey/uiState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAaH,6DAA6D;AAC7D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAI5D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAGhD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,SAAS,CA2BrD"}
|
|
@@ -82,10 +82,16 @@ export function uiStateSignature() {
|
|
|
82
82
|
return undefined;
|
|
83
83
|
/* SORTED, or the same surface produces different signatures depending on DOM order and every
|
|
84
84
|
count fragments into several that mean nothing. */
|
|
85
|
-
|
|
85
|
+
const pairs = [...dims.keys()]
|
|
86
86
|
.sort()
|
|
87
|
-
.map((k) => `${k}:${dims.get(k)}`)
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
.map((k) => `${k}:${dims.get(k)}`);
|
|
88
|
+
let signature = '';
|
|
89
|
+
for (const pair of pairs) {
|
|
90
|
+
const next = signature ? `${signature};${pair}` : pair;
|
|
91
|
+
if (next.length > 96)
|
|
92
|
+
break;
|
|
93
|
+
signature = next;
|
|
94
|
+
}
|
|
95
|
+
return signature || undefined;
|
|
90
96
|
}
|
|
91
97
|
//# sourceMappingURL=uiState.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uiState.js","sourceRoot":"","sources":["../../../src/features/journey/uiState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,qEAAqE;AACrE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;AAE3C;;;;GAIG;AACH,MAAM,KAAK,GAAG,CAAC,CAAS,EAAU,EAAE,CAClC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAE3E,6DAA6D;AAC7D,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAa;IACpD,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACvB,IAAI,CAAC,IAAI,CAAC;QAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,IAAI,IAAI,KAAK,SAAS;QAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;;QACpC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,QAAQ,CAAC,gBAAgB,CAAC,wBAAwB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QAChE,MAAM,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;QACrF,6FAA6F;QAC7F,2FAA2F;QAC3F,kDAAkD;QAClD,MAAM,CAAC,GAAG,KAAK,CACb,CAAC,CAAC,YAAY,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,CACjF,CAAC;QACF,IAAI,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,QAAQ;QAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9C,IAAI,CAAC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IACjC;yDACqD;IACrD,
|
|
1
|
+
{"version":3,"file":"uiState.js","sourceRoot":"","sources":["../../../src/features/journey/uiState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,qEAAqE;AACrE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;AAE3C;;;;GAIG;AACH,MAAM,KAAK,GAAG,CAAC,CAAS,EAAU,EAAE,CAClC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAE3E,6DAA6D;AAC7D,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAa;IACpD,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IACtB,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACvB,IAAI,CAAC,IAAI,CAAC;QAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,IAAI,IAAI,KAAK,SAAS;QAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;;QACpC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,QAAQ,CAAC,gBAAgB,CAAC,wBAAwB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;QAChE,MAAM,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;QACrF,6FAA6F;QAC7F,2FAA2F;QAC3F,kDAAkD;QAClD,MAAM,CAAC,GAAG,KAAK,CACb,CAAC,CAAC,YAAY,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,CACjF,CAAC;QACF,IAAI,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,QAAQ;QAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9C,IAAI,CAAC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IACjC;yDACqD;IACrD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;SAC3B,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACrC,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACvD,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE;YAAE,MAAM;QAC5B,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IACD,OAAO,SAAS,IAAI,SAAS,CAAC;AAChC,CAAC"}
|