@agent-native/core 0.98.3 → 0.98.4
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +13 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/analytics.ts +12 -0
- package/corpus/core/src/client/session-replay.ts +612 -26
- package/corpus/core/src/client/settings/DemoModeSection.tsx +3 -3
- package/corpus/core/src/demo/actions/toggle-demo-mode.ts +2 -2
- package/corpus/core/src/demo/fetch-interceptor.ts +25 -2
- package/corpus/core/src/demo/redact.ts +58 -9
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +46 -4
- package/corpus/templates/analytics/app/global.css +4 -62
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +225 -52
- package/corpus/templates/analytics/changelog/2026-07-12-fixed-session-replays-that-appeared-ultra-wide-hid-recorded-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-session-replays-load-faster-in-demo-mode-while-visitor-email.md +6 -0
- package/corpus/templates/calendar/app/components/calendar/DeleteEventDialog.tsx +11 -2
- package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +14 -2
- package/corpus/templates/calendar/changelog/2026-07-12-calendar-responses-can-now-be-saved-with-command-enter-or-ct.md +6 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +11 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/session-replay.d.ts +12 -3
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +488 -23
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/settings/DemoModeSection.js +1 -1
- package/dist/client/settings/DemoModeSection.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/demo/actions/toggle-demo-mode.js +2 -2
- package/dist/demo/actions/toggle-demo-mode.js.map +1 -1
- package/dist/demo/fetch-interceptor.d.ts +1 -0
- package/dist/demo/fetch-interceptor.d.ts.map +1 -1
- package/dist/demo/fetch-interceptor.js +19 -2
- package/dist/demo/fetch-interceptor.js.map +1 -1
- package/dist/demo/redact.d.ts +7 -0
- package/dist/demo/redact.d.ts.map +1 -1
- package/dist/demo/redact.js +19 -10
- package/dist/demo/redact.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
|
@@ -168,6 +168,11 @@ const DEFAULT_PLAYER_WIDTH = 1024;
|
|
|
168
168
|
const DEFAULT_PLAYER_HEIGHT = 640;
|
|
169
169
|
const MALFORMED_REPLAY_MIN_WIDTH = 4000;
|
|
170
170
|
const MALFORMED_REPLAY_MIN_ASPECT_RATIO = 4;
|
|
171
|
+
// Evidence-backed exception for the one pre-fix viewport shape observed in a
|
|
172
|
+
// stored recording. Do not broaden this to an aspect-ratio range: valid
|
|
173
|
+
// 3440x900 ultrawide browser viewports occupy the same range.
|
|
174
|
+
const LEGACY_MALFORMED_REPLAY_WIDTH = 3189;
|
|
175
|
+
const LEGACY_MALFORMED_REPLAY_HEIGHT = 885;
|
|
171
176
|
const MIN_REPLAY_DISPLAY_DIMENSION = 240;
|
|
172
177
|
const RECOVERED_REPLAY_DISPLAY_ASPECT_RATIO =
|
|
173
178
|
DEFAULT_PLAYER_WIDTH / DEFAULT_PLAYER_HEIGHT;
|
|
@@ -186,10 +191,14 @@ const MIN_STAGE_HEIGHT_PX = 240;
|
|
|
186
191
|
const SCRUBBER_MARKER_LIMIT = 500;
|
|
187
192
|
const TIMELINE_MARKER_LIMIT = 300;
|
|
188
193
|
const TIMELINE_FOLLOW_PAUSE_MS = 4000;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
194
|
+
const REPLAY_CLOCK_UPDATE_INTERVAL_MS = 100;
|
|
195
|
+
/**
|
|
196
|
+
* Keep captured overlays intact. Toasts and snackbars are product feedback,
|
|
197
|
+
* not recorder chrome, and can be essential to understanding the session.
|
|
198
|
+
* The recorder does not inject notification UI into the recorded document, so
|
|
199
|
+
* blanket selectors (including Sonner's data attributes) are not justified.
|
|
200
|
+
*/
|
|
201
|
+
export const REPLAY_OVERLAY_STYLE_RULES: string[] = [];
|
|
193
202
|
type ReplayConsoleDiagnostics = ReturnType<
|
|
194
203
|
typeof extractReplayDiagnostics
|
|
195
204
|
>["console"];
|
|
@@ -235,11 +244,13 @@ const RRWEB_EVENT_TYPE = {
|
|
|
235
244
|
|
|
236
245
|
const INCREMENTAL_SOURCE = {
|
|
237
246
|
Mutation: 0,
|
|
247
|
+
MouseMove: 1,
|
|
238
248
|
MouseInteraction: 2,
|
|
239
249
|
Scroll: 3,
|
|
240
250
|
ViewportResize: 4,
|
|
241
251
|
Input: 5,
|
|
242
252
|
TouchMove: 6,
|
|
253
|
+
Drag: 12,
|
|
243
254
|
} as const;
|
|
244
255
|
|
|
245
256
|
const MOUSE_INTERACTION = {
|
|
@@ -480,6 +491,7 @@ function ReplayPlayer({
|
|
|
480
491
|
const stageRootRef = useRef<HTMLDivElement>(null);
|
|
481
492
|
const replayerRef = useRef<any>(null);
|
|
482
493
|
const rafRef = useRef<number | null>(null);
|
|
494
|
+
const lastClockUpdateAtRef = useRef<number | null>(null);
|
|
483
495
|
const [status, setStatus] = useState<ReplayPlayerStatus>("idle");
|
|
484
496
|
const [error, setError] = useState<string | null>(null);
|
|
485
497
|
const [playing, setPlaying] = useState(false);
|
|
@@ -520,12 +532,13 @@ function ReplayPlayer({
|
|
|
520
532
|
const scrubResumePlayingRef = useRef(false);
|
|
521
533
|
|
|
522
534
|
// IMPORTANT — DO NOT REMOVE THIS LEGACY VIEWPORT CORRECTION.
|
|
523
|
-
// Some stored sessions contain impossible
|
|
535
|
+
// Some stored sessions contain impossible 3,000–7,500px-wide Meta/resize
|
|
524
536
|
// values even though the browser was a normal desktop viewport. Rendering
|
|
525
537
|
// those raw values collapses the replay into a tiny horizontal ribbon. Keep
|
|
526
538
|
// normal recordings exact—including 32:9 and mobile portrait. Recover only
|
|
527
|
-
// widths >=4,000px with aspect >4:1
|
|
528
|
-
//
|
|
539
|
+
// widths >=4,000px with aspect >4:1, plus the exact known 3,189x885 legacy
|
|
540
|
+
// shape, to the standard 16:10 viewport. The same dimensions are applied to
|
|
541
|
+
// rrweb's iframe so CSS breakpoints/camera agree.
|
|
529
542
|
const displayDims = clampReplayDisplayDimensions(streamedDims ?? initialDims);
|
|
530
543
|
const playerWidth = displayDims?.width ?? DEFAULT_PLAYER_WIDTH;
|
|
531
544
|
const playerHeight = displayDims?.height ?? DEFAULT_PLAYER_HEIGHT;
|
|
@@ -624,10 +637,16 @@ function ReplayPlayer({
|
|
|
624
637
|
|
|
625
638
|
const updateTime = useCallback(
|
|
626
639
|
(next: number) => {
|
|
640
|
+
if (!Number.isFinite(next) || next === currentTimeRef.current) return;
|
|
641
|
+
// Keep the live value in sync before React commits. The animation clock
|
|
642
|
+
// can run again while React is still processing the previous render;
|
|
643
|
+
// relying on useLiveRef's effect here republishes the same value and can
|
|
644
|
+
// create a nested update loop in development.
|
|
645
|
+
currentTimeRef.current = next;
|
|
627
646
|
setCurrentTime(next);
|
|
628
647
|
onTimeUpdate(next);
|
|
629
648
|
},
|
|
630
|
-
[onTimeUpdate],
|
|
649
|
+
[currentTimeRef, onTimeUpdate],
|
|
631
650
|
);
|
|
632
651
|
|
|
633
652
|
const seek = useCallback(
|
|
@@ -709,27 +728,17 @@ function ReplayPlayer({
|
|
|
709
728
|
let localReplayer: any = null;
|
|
710
729
|
|
|
711
730
|
async function loadReplay() {
|
|
712
|
-
// Wait for the full recording before creating the Replayer. Progressive
|
|
713
|
-
// chunk publishes used to rebuild the player on every append, which
|
|
714
|
-
// reset the clock, disabled the scrubber, and desynced the playhead.
|
|
715
731
|
const replayEvents = eventsRef.current;
|
|
732
|
+
// Replayer construction remains all-or-nothing. Progressive response
|
|
733
|
+
// publishes update loading progress only; rebuilding during download
|
|
734
|
+
// rewinds the player and can desync the scrubber/playhead.
|
|
716
735
|
if (!response.isComplete) {
|
|
717
736
|
setStatus("loading");
|
|
718
737
|
setError(null);
|
|
719
738
|
setPlaying(false);
|
|
720
739
|
return;
|
|
721
740
|
}
|
|
722
|
-
if (replayEvents.length < 2) {
|
|
723
|
-
throw new Error(t("sessions.noReplayEvents"));
|
|
724
|
-
}
|
|
725
|
-
if (
|
|
726
|
-
!replayEvents.some(
|
|
727
|
-
(event) => event.type === RRWEB_EVENT_TYPE.FullSnapshot,
|
|
728
|
-
)
|
|
729
|
-
) {
|
|
730
|
-
throw new Error(t("sessions.noReplayEvents"));
|
|
731
|
-
}
|
|
732
|
-
if (!hasPlayableReplayEvents(replayEvents)) {
|
|
741
|
+
if (replayEvents.length < 2 || !hasPlayableReplayEvents(replayEvents)) {
|
|
733
742
|
throw new Error(t("sessions.noReplayEvents"));
|
|
734
743
|
}
|
|
735
744
|
setStatus("loading");
|
|
@@ -747,15 +756,27 @@ function ReplayPlayer({
|
|
|
747
756
|
const rawInitialDims = replayInitialViewportDimensions(replayEvents);
|
|
748
757
|
const correctedInitialDims = replayInitialDisplayDimensions(replayEvents);
|
|
749
758
|
setStreamedDims(correctedInitialDims);
|
|
750
|
-
|
|
759
|
+
// The legacy malformed-width recordings need one paired exception to
|
|
760
|
+
// raw-event pass-through: pointer coordinates were captured in the same
|
|
761
|
+
// impossible viewport coordinate space. Correcting only the iframe makes
|
|
762
|
+
// rrweb cast clicks thousands of pixels off-stage. Project pointer data
|
|
763
|
+
// with each malformed Meta/resize while retaining every other raw field.
|
|
764
|
+
const playerEvents = projectReplayPointerCoordinates(replayEvents);
|
|
765
|
+
// Keep this loosely typed because the legacy viewport recovery below
|
|
766
|
+
// intentionally calls rrweb's runtime handleResize hook, which its
|
|
767
|
+
// TypeScript declaration marks private.
|
|
768
|
+
localReplayer = new Replayer(playerEvents as any[], {
|
|
751
769
|
root: stageRootRef.current,
|
|
752
770
|
speed: speedRef.current,
|
|
753
771
|
skipInactive: false,
|
|
754
772
|
showWarning: false,
|
|
755
773
|
showDebug: false,
|
|
756
|
-
triggerFocus: false,
|
|
757
774
|
mouseTail: false,
|
|
758
|
-
|
|
775
|
+
// Match stock rrweb/builder-internal focus replay. Disabling focus
|
|
776
|
+
// drops recorded focus-visible state and can leave menus/forms looking
|
|
777
|
+
// unlike the source page even when the snapshot CSS is correct.
|
|
778
|
+
triggerFocus: true,
|
|
779
|
+
insertStyleRules: REPLAY_OVERLAY_STYLE_RULES,
|
|
759
780
|
});
|
|
760
781
|
// IMPORTANT: rrweb constructs its iframe from the raw initial Meta event.
|
|
761
782
|
// When that legacy Meta width is malformed, correcting only streamedDims
|
|
@@ -766,7 +787,7 @@ function ReplayPlayer({
|
|
|
766
787
|
(rawInitialDims?.width !== correctedInitialDims.width ||
|
|
767
788
|
rawInitialDims?.height !== correctedInitialDims.height)
|
|
768
789
|
) {
|
|
769
|
-
localReplayer.handleResize?.(correctedInitialDims);
|
|
790
|
+
(localReplayer as any).handleResize?.(correctedInitialDims);
|
|
770
791
|
}
|
|
771
792
|
// rrweb already sandboxes the replay document without script execution.
|
|
772
793
|
// Do not mutate recorded URLs/CSS; suppress viewer-page referrer leakage
|
|
@@ -805,6 +826,17 @@ function ReplayPlayer({
|
|
|
805
826
|
// rrweb handles the raw resize before notifying us. Override only
|
|
806
827
|
// impossible legacy geometry so its iframe and our stage stay equal.
|
|
807
828
|
// DO NOT clamp just one layer; that breaks responsive CSS and clicks.
|
|
829
|
+
if (
|
|
830
|
+
correctedDims.width !== rawDims.width ||
|
|
831
|
+
correctedDims.height !== rawDims.height
|
|
832
|
+
) {
|
|
833
|
+
// rrweb applies the raw Meta/ViewportResize to its iframe before
|
|
834
|
+
// emitting this callback. Re-apply the correction every time the
|
|
835
|
+
// raw event is malformed, even when React already stores the same
|
|
836
|
+
// corrected dimensions. Returning early here recreates the exact
|
|
837
|
+
// bug where the outer stage is normal but the iframe is ultra-wide.
|
|
838
|
+
(localReplayer as any).handleResize?.(correctedDims);
|
|
839
|
+
}
|
|
808
840
|
const currentDims = streamedDimsRef.current;
|
|
809
841
|
if (
|
|
810
842
|
currentDims?.width === correctedDims.width &&
|
|
@@ -812,12 +844,6 @@ function ReplayPlayer({
|
|
|
812
844
|
) {
|
|
813
845
|
return;
|
|
814
846
|
}
|
|
815
|
-
if (
|
|
816
|
-
correctedDims.width !== rawDims.width ||
|
|
817
|
-
correctedDims.height !== rawDims.height
|
|
818
|
-
) {
|
|
819
|
-
localReplayer.handleResize?.(correctedDims);
|
|
820
|
-
}
|
|
821
847
|
setStreamedDims(correctedDims);
|
|
822
848
|
}
|
|
823
849
|
});
|
|
@@ -851,6 +877,7 @@ function ReplayPlayer({
|
|
|
851
877
|
try {
|
|
852
878
|
localReplayer?.pause?.();
|
|
853
879
|
localReplayer?.destroy?.();
|
|
880
|
+
replayerRef.current?.pause?.();
|
|
854
881
|
replayerRef.current?.destroy?.();
|
|
855
882
|
} catch {
|
|
856
883
|
// rrweb cleanup is best-effort across versions.
|
|
@@ -858,9 +885,6 @@ function ReplayPlayer({
|
|
|
858
885
|
replayerRef.current = null;
|
|
859
886
|
if (stageRootRef.current) stageRootRef.current.innerHTML = "";
|
|
860
887
|
};
|
|
861
|
-
// Key only on isComplete + a stable events identity. Including the events
|
|
862
|
-
// array reference would rebuild the Replayer when the final publish()
|
|
863
|
-
// replaces the chunks object even though the event set is unchanged.
|
|
864
888
|
}, [
|
|
865
889
|
currentTimeRef,
|
|
866
890
|
eventsIdentity,
|
|
@@ -875,10 +899,11 @@ function ReplayPlayer({
|
|
|
875
899
|
if (!playing || status !== "ready") {
|
|
876
900
|
if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
|
|
877
901
|
rafRef.current = null;
|
|
902
|
+
lastClockUpdateAtRef.current = null;
|
|
878
903
|
return;
|
|
879
904
|
}
|
|
880
905
|
|
|
881
|
-
const tick = () => {
|
|
906
|
+
const tick = (frameTime: number) => {
|
|
882
907
|
const replayer = replayerRef.current;
|
|
883
908
|
if (replayer && !scrubbingRef.current) {
|
|
884
909
|
let nextTime = Number(
|
|
@@ -901,7 +926,17 @@ function ReplayPlayer({
|
|
|
901
926
|
}
|
|
902
927
|
}
|
|
903
928
|
}
|
|
904
|
-
if (
|
|
929
|
+
if (
|
|
930
|
+
shouldPublishReplayClockUpdate(
|
|
931
|
+
lastClockUpdateAtRef.current,
|
|
932
|
+
frameTime,
|
|
933
|
+
currentTimeRef.current,
|
|
934
|
+
nextTime,
|
|
935
|
+
)
|
|
936
|
+
) {
|
|
937
|
+
lastClockUpdateAtRef.current = frameTime;
|
|
938
|
+
updateTime(nextTime);
|
|
939
|
+
}
|
|
905
940
|
}
|
|
906
941
|
rafRef.current = requestAnimationFrame(tick);
|
|
907
942
|
};
|
|
@@ -910,6 +945,7 @@ function ReplayPlayer({
|
|
|
910
945
|
return () => {
|
|
911
946
|
if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
|
|
912
947
|
rafRef.current = null;
|
|
948
|
+
lastClockUpdateAtRef.current = null;
|
|
913
949
|
};
|
|
914
950
|
}, [
|
|
915
951
|
currentTimeRef,
|
|
@@ -995,6 +1031,10 @@ function ReplayPlayer({
|
|
|
995
1031
|
/>
|
|
996
1032
|
<button
|
|
997
1033
|
type="button"
|
|
1034
|
+
// IMPORTANT: Keep the viewer's real OS pointer visible while
|
|
1035
|
+
// the synthetic rrweb pointer replays underneath it. Hiding
|
|
1036
|
+
// this during playback makes the page feel broken whenever
|
|
1037
|
+
// the viewer moves their mouse over the full-stage control.
|
|
998
1038
|
className="absolute inset-0 z-20 cursor-pointer rounded-[inherit] border-0 bg-transparent p-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-default"
|
|
999
1039
|
disabled={disabled}
|
|
1000
1040
|
aria-label={
|
|
@@ -1954,6 +1994,102 @@ export function normalizeReplayEvents(events: unknown[]): AnyReplayEvent[] {
|
|
|
1954
1994
|
.sort((a, b) => Number(a.timestamp ?? 0) - Number(b.timestamp ?? 0));
|
|
1955
1995
|
}
|
|
1956
1996
|
|
|
1997
|
+
/**
|
|
1998
|
+
* Pair pointer coordinates with the narrowly gated legacy viewport recovery.
|
|
1999
|
+
*
|
|
2000
|
+
* IMPORTANT: do not apply this to normal recordings and do not rewrite the
|
|
2001
|
+
* Meta/resize events themselves. rrweb must still rebuild the captured DOM
|
|
2002
|
+
* from raw events; only pointer coordinates need projection into the corrected
|
|
2003
|
+
* iframe camera. Returning original references for untouched events keeps this
|
|
2004
|
+
* compatible with stock rrweb and avoids cloning large snapshots.
|
|
2005
|
+
*/
|
|
2006
|
+
export function projectReplayPointerCoordinates(
|
|
2007
|
+
events: AnyReplayEvent[],
|
|
2008
|
+
): AnyReplayEvent[] {
|
|
2009
|
+
let rawDims: ReplayViewportDimensions | null = null;
|
|
2010
|
+
let displayDims: ReplayViewportDimensions | null = null;
|
|
2011
|
+
|
|
2012
|
+
return events.map((event) => {
|
|
2013
|
+
const nextDims = dimensionsFromReplayEvent(event);
|
|
2014
|
+
if (nextDims) {
|
|
2015
|
+
rawDims = nextDims;
|
|
2016
|
+
displayDims = clampReplayDisplayDimensions(nextDims);
|
|
2017
|
+
return event;
|
|
2018
|
+
}
|
|
2019
|
+
if (
|
|
2020
|
+
!rawDims ||
|
|
2021
|
+
!displayDims ||
|
|
2022
|
+
(rawDims.width === displayDims.width &&
|
|
2023
|
+
rawDims.height === displayDims.height) ||
|
|
2024
|
+
event.type !== RRWEB_EVENT_TYPE.IncrementalSnapshot
|
|
2025
|
+
) {
|
|
2026
|
+
return event;
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
const source = event.data?.source;
|
|
2030
|
+
if (source === INCREMENTAL_SOURCE.MouseInteraction) {
|
|
2031
|
+
const data = projectReplayPointerData(event.data, rawDims, displayDims);
|
|
2032
|
+
return data === event.data ? event : { ...event, data };
|
|
2033
|
+
}
|
|
2034
|
+
if (
|
|
2035
|
+
source !== INCREMENTAL_SOURCE.MouseMove &&
|
|
2036
|
+
source !== INCREMENTAL_SOURCE.TouchMove &&
|
|
2037
|
+
source !== INCREMENTAL_SOURCE.Drag
|
|
2038
|
+
) {
|
|
2039
|
+
return event;
|
|
2040
|
+
}
|
|
2041
|
+
const positions = Array.isArray(event.data?.positions)
|
|
2042
|
+
? event.data.positions
|
|
2043
|
+
: [];
|
|
2044
|
+
let changed = false;
|
|
2045
|
+
const projectedPositions = positions.map((position: unknown) => {
|
|
2046
|
+
if (!isRecord(position)) return position;
|
|
2047
|
+
const projected = projectReplayPointerData(
|
|
2048
|
+
position,
|
|
2049
|
+
rawDims!,
|
|
2050
|
+
displayDims!,
|
|
2051
|
+
);
|
|
2052
|
+
if (projected !== position) changed = true;
|
|
2053
|
+
return projected;
|
|
2054
|
+
});
|
|
2055
|
+
return changed
|
|
2056
|
+
? { ...event, data: { ...event.data, positions: projectedPositions } }
|
|
2057
|
+
: event;
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
function projectReplayPointerData(
|
|
2062
|
+
data: AnyReplayEvent,
|
|
2063
|
+
rawDims: ReplayViewportDimensions,
|
|
2064
|
+
displayDims: ReplayViewportDimensions,
|
|
2065
|
+
): AnyReplayEvent {
|
|
2066
|
+
const projectedX = projectReplayCoordinate(
|
|
2067
|
+
data.x,
|
|
2068
|
+
displayDims.width / rawDims.width,
|
|
2069
|
+
displayDims.width,
|
|
2070
|
+
);
|
|
2071
|
+
const projectedY = projectReplayCoordinate(
|
|
2072
|
+
data.y,
|
|
2073
|
+
displayDims.height / rawDims.height,
|
|
2074
|
+
displayDims.height,
|
|
2075
|
+
);
|
|
2076
|
+
if (projectedX === data.x && projectedY === data.y) return data;
|
|
2077
|
+
return {
|
|
2078
|
+
...data,
|
|
2079
|
+
...(projectedX !== undefined ? { x: projectedX } : {}),
|
|
2080
|
+
...(projectedY !== undefined ? { y: projectedY } : {}),
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
function projectReplayCoordinate(
|
|
2085
|
+
value: unknown,
|
|
2086
|
+
scale: number,
|
|
2087
|
+
maximum: number,
|
|
2088
|
+
): number | undefined {
|
|
2089
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
|
|
2090
|
+
return clamp(value * scale, 0, Math.max(0, maximum - 1));
|
|
2091
|
+
}
|
|
2092
|
+
|
|
1957
2093
|
export function buildReplayMarkers(events: AnyReplayEvent[]): ReplayMarker[] {
|
|
1958
2094
|
const startedAt = replayStartedAt(events);
|
|
1959
2095
|
const markers: ReplayMarker[] = [];
|
|
@@ -2169,9 +2305,7 @@ export function buildIdleSkipRanges(events: AnyReplayEvent[]): SkipRange[] {
|
|
|
2169
2305
|
const timestamp = Number(event.timestamp ?? 0);
|
|
2170
2306
|
if (!Number.isFinite(timestamp) || timestamp <= 0) continue;
|
|
2171
2307
|
lastTimestamp = Math.max(lastTimestamp, timestamp);
|
|
2172
|
-
|
|
2173
|
-
interactions.push(timestamp);
|
|
2174
|
-
}
|
|
2308
|
+
interactions.push(...replayActivityTimestamps(event, timestamp));
|
|
2175
2309
|
}
|
|
2176
2310
|
interactions.sort((a, b) => a - b);
|
|
2177
2311
|
const ranges: SkipRange[] = [];
|
|
@@ -2194,22 +2328,42 @@ export function buildIdleSkipRanges(events: AnyReplayEvent[]): SkipRange[] {
|
|
|
2194
2328
|
return ranges;
|
|
2195
2329
|
}
|
|
2196
2330
|
|
|
2197
|
-
function
|
|
2198
|
-
|
|
2199
|
-
|
|
2331
|
+
function replayActivityTimestamps(
|
|
2332
|
+
event: AnyReplayEvent,
|
|
2333
|
+
timestamp: number,
|
|
2334
|
+
): number[] {
|
|
2335
|
+
if (event.type === RRWEB_EVENT_TYPE.Meta) return [timestamp];
|
|
2336
|
+
if (event.type !== RRWEB_EVENT_TYPE.IncrementalSnapshot) return [];
|
|
2200
2337
|
|
|
2201
2338
|
const source = event.data?.source;
|
|
2339
|
+
if (
|
|
2340
|
+
source === INCREMENTAL_SOURCE.MouseMove ||
|
|
2341
|
+
source === INCREMENTAL_SOURCE.TouchMove ||
|
|
2342
|
+
source === INCREMENTAL_SOURCE.Drag
|
|
2343
|
+
) {
|
|
2344
|
+
const positions = Array.isArray(event.data?.positions)
|
|
2345
|
+
? event.data.positions
|
|
2346
|
+
: [];
|
|
2347
|
+
// IMPORTANT: rrweb batches pointer positions and schedules each one at
|
|
2348
|
+
// event.timestamp + timeOffset. Treat those exact moments as activity.
|
|
2349
|
+
// Ignoring the batch made Skip inactivity jump across visible movement,
|
|
2350
|
+
// which looked like a frozen cursor even though the recording was intact.
|
|
2351
|
+
return positions.flatMap((position: unknown) => {
|
|
2352
|
+
if (!isRecord(position)) return [];
|
|
2353
|
+
const timeOffset = Number(position.timeOffset ?? 0);
|
|
2354
|
+
return [timestamp + (Number.isFinite(timeOffset) ? timeOffset : 0)];
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2202
2357
|
if (
|
|
2203
2358
|
source === INCREMENTAL_SOURCE.Input ||
|
|
2204
|
-
source === INCREMENTAL_SOURCE.Scroll
|
|
2205
|
-
source === INCREMENTAL_SOURCE.TouchMove
|
|
2359
|
+
source === INCREMENTAL_SOURCE.Scroll
|
|
2206
2360
|
) {
|
|
2207
|
-
return
|
|
2361
|
+
return [timestamp];
|
|
2208
2362
|
}
|
|
2209
|
-
if (source !== INCREMENTAL_SOURCE.MouseInteraction) return
|
|
2363
|
+
if (source !== INCREMENTAL_SOURCE.MouseInteraction) return [];
|
|
2210
2364
|
|
|
2211
2365
|
const interactionType = event.data?.type;
|
|
2212
|
-
return
|
|
2366
|
+
return [
|
|
2213
2367
|
interactionType === MOUSE_INTERACTION.MouseUp ||
|
|
2214
2368
|
interactionType === MOUSE_INTERACTION.MouseDown ||
|
|
2215
2369
|
interactionType === MOUSE_INTERACTION.Click ||
|
|
@@ -2218,7 +2372,9 @@ function isReplayActivityEvent(event: AnyReplayEvent): boolean {
|
|
|
2218
2372
|
interactionType === MOUSE_INTERACTION.Focus ||
|
|
2219
2373
|
interactionType === MOUSE_INTERACTION.TouchStart ||
|
|
2220
2374
|
interactionType === MOUSE_INTERACTION.TouchEnd
|
|
2221
|
-
|
|
2375
|
+
? timestamp
|
|
2376
|
+
: null,
|
|
2377
|
+
].filter((value): value is number => value !== null);
|
|
2222
2378
|
}
|
|
2223
2379
|
|
|
2224
2380
|
function pushIdleRange(
|
|
@@ -2395,10 +2551,13 @@ export function clampReplayDisplayDimensions(
|
|
|
2395
2551
|
return { width: DEFAULT_PLAYER_WIDTH, height: DEFAULT_PLAYER_HEIGHT };
|
|
2396
2552
|
}
|
|
2397
2553
|
const aspect = dims.width / dims.height;
|
|
2398
|
-
|
|
2554
|
+
const hasMalformedWideGeometry =
|
|
2399
2555
|
dims.width >= MALFORMED_REPLAY_MIN_WIDTH &&
|
|
2400
|
-
aspect > MALFORMED_REPLAY_MIN_ASPECT_RATIO
|
|
2401
|
-
|
|
2556
|
+
aspect > MALFORMED_REPLAY_MIN_ASPECT_RATIO;
|
|
2557
|
+
const hasLegacyMalformedWideGeometry =
|
|
2558
|
+
dims.width === LEGACY_MALFORMED_REPLAY_WIDTH &&
|
|
2559
|
+
dims.height === LEGACY_MALFORMED_REPLAY_HEIGHT;
|
|
2560
|
+
if (hasMalformedWideGeometry || hasLegacyMalformedWideGeometry) {
|
|
2402
2561
|
return {
|
|
2403
2562
|
width: Math.round(dims.height * RECOVERED_REPLAY_DISPLAY_ASPECT_RATIO),
|
|
2404
2563
|
height: dims.height,
|
|
@@ -2563,6 +2722,20 @@ function clamp(value: number, min: number, max: number): number {
|
|
|
2563
2722
|
return Math.min(max, Math.max(min, value));
|
|
2564
2723
|
}
|
|
2565
2724
|
|
|
2725
|
+
export function shouldPublishReplayClockUpdate(
|
|
2726
|
+
lastUpdateAt: number | null,
|
|
2727
|
+
frameTime: number,
|
|
2728
|
+
currentTime: number,
|
|
2729
|
+
nextTime: number,
|
|
2730
|
+
): boolean {
|
|
2731
|
+
if (!Number.isFinite(frameTime) || !Number.isFinite(nextTime)) return false;
|
|
2732
|
+
if (nextTime === currentTime) return false;
|
|
2733
|
+
return (
|
|
2734
|
+
lastUpdateAt == null ||
|
|
2735
|
+
frameTime - lastUpdateAt >= REPLAY_CLOCK_UPDATE_INTERVAL_MS
|
|
2736
|
+
);
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2566
2739
|
function isRecord(value: unknown): value is AnyRecord {
|
|
2567
2740
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
2568
2741
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useT } from "@agent-native/core/client";
|
|
2
2
|
import type { CalendarEvent, DeleteEventScope } from "@shared/api";
|
|
3
|
-
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
|
|
5
5
|
import { getGuestAttendeeCount } from "@/components/calendar/GuestNotificationDialog";
|
|
6
6
|
import {
|
|
@@ -36,6 +36,7 @@ export function DeleteEventDialog({
|
|
|
36
36
|
onConfirm,
|
|
37
37
|
}: DeleteEventDialogProps) {
|
|
38
38
|
const t = useT();
|
|
39
|
+
const confirmButtonRef = useRef<HTMLButtonElement>(null);
|
|
39
40
|
const [scope, setScope] = useState<DeleteEventScope>("single");
|
|
40
41
|
const [message, setMessage] = useState("");
|
|
41
42
|
|
|
@@ -108,7 +109,14 @@ export function DeleteEventDialog({
|
|
|
108
109
|
|
|
109
110
|
return (
|
|
110
111
|
<AlertDialog open={open} onOpenChange={(isOpen) => !isOpen && onClose()}>
|
|
111
|
-
<AlertDialogContent
|
|
112
|
+
<AlertDialogContent
|
|
113
|
+
className="max-w-[420px]"
|
|
114
|
+
onKeyDown={handleKeyDown}
|
|
115
|
+
onOpenAutoFocus={(event) => {
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
confirmButtonRef.current?.focus();
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
112
120
|
<AlertDialogHeader>
|
|
113
121
|
<AlertDialogTitle className="text-sm">{copy.title}</AlertDialogTitle>
|
|
114
122
|
<AlertDialogDescription>{copy.description}</AlertDialogDescription>
|
|
@@ -174,6 +182,7 @@ export function DeleteEventDialog({
|
|
|
174
182
|
</Button>
|
|
175
183
|
)}
|
|
176
184
|
<Button
|
|
185
|
+
ref={confirmButtonRef}
|
|
177
186
|
variant="destructive"
|
|
178
187
|
onClick={() => handleConfirm(canNotifyGuests ? "all" : "none")}
|
|
179
188
|
>
|
|
@@ -194,6 +194,12 @@ function RsvpControls({
|
|
|
194
194
|
doRsvp(status, undefined, "");
|
|
195
195
|
};
|
|
196
196
|
|
|
197
|
+
const savePendingResponse = () => {
|
|
198
|
+
if (!pendingStatus || mutation.isPending) return;
|
|
199
|
+
doRsvp(pendingStatus, isRecurring ? pendingScope : undefined);
|
|
200
|
+
closePopover();
|
|
201
|
+
};
|
|
202
|
+
|
|
197
203
|
return (
|
|
198
204
|
<Popover
|
|
199
205
|
open={!!pendingStatus}
|
|
@@ -255,6 +261,13 @@ function RsvpControls({
|
|
|
255
261
|
className="w-[22rem] max-w-[calc(100vw-2rem)] overflow-hidden p-0"
|
|
256
262
|
onClick={(e) => e.stopPropagation()}
|
|
257
263
|
onPointerDownCapture={(e) => e.stopPropagation()}
|
|
264
|
+
onKeyDown={(e) => {
|
|
265
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
|
|
266
|
+
e.preventDefault();
|
|
267
|
+
e.stopPropagation();
|
|
268
|
+
savePendingResponse();
|
|
269
|
+
}
|
|
270
|
+
}}
|
|
258
271
|
>
|
|
259
272
|
{pendingStatus && (
|
|
260
273
|
<div>
|
|
@@ -336,8 +349,7 @@ function RsvpControls({
|
|
|
336
349
|
disabled={mutation.isPending}
|
|
337
350
|
onClick={(e) => {
|
|
338
351
|
e.stopPropagation();
|
|
339
|
-
|
|
340
|
-
closePopover();
|
|
352
|
+
savePendingResponse();
|
|
341
353
|
}}
|
|
342
354
|
>
|
|
343
355
|
{t("eventForm.saveResponse")}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/client/analytics.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/client/analytics.ts"],"names":[],"mappings":"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"}
|
package/dist/client/analytics.js
CHANGED
|
@@ -976,6 +976,17 @@ function configuredSessionReplayOptions(config, tracking = {}) {
|
|
|
976
976
|
...(publicKey && !options.publicKey ? { publicKey } : {}),
|
|
977
977
|
...(endpoint && !options.endpoint ? { endpoint } : {}),
|
|
978
978
|
...options,
|
|
979
|
+
onUploadRejected: options.onUploadRejected ??
|
|
980
|
+
((details) => {
|
|
981
|
+
trackEvent("session replay upload rejected", {
|
|
982
|
+
status: details.status,
|
|
983
|
+
restart_attempted: details.restartAttempted,
|
|
984
|
+
restart_succeeded: details.restartSucceeded,
|
|
985
|
+
...(details.restartReason
|
|
986
|
+
? { restart_reason: details.restartReason }
|
|
987
|
+
: {}),
|
|
988
|
+
});
|
|
989
|
+
}),
|
|
979
990
|
requireSignedInUser: options.requireSignedInUser ??
|
|
980
991
|
sessionReplayRequiresSignedInUserFromEnv() ??
|
|
981
992
|
true,
|