@brett_lamy/docstream 1.6.0 → 1.7.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.
Files changed (43) hide show
  1. package/README.md +34 -7
  2. package/dist/index.d.ts +2 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/replay/ReplayPlayer.d.ts +64 -0
  7. package/dist/replay/ReplayPlayer.d.ts.map +1 -0
  8. package/dist/replay/ReplayPlayer.js +211 -0
  9. package/dist/replay/ReplayPlayer.js.map +1 -0
  10. package/dist/replay/ReplayPreview.d.ts +24 -12
  11. package/dist/replay/ReplayPreview.d.ts.map +1 -1
  12. package/dist/replay/ReplayPreview.js +37 -32
  13. package/dist/replay/ReplayPreview.js.map +1 -1
  14. package/dist/replay/ReplayStage.d.ts +1 -1
  15. package/dist/replay/ReplayStage.js +1 -1
  16. package/dist/replay/ReplayStageImpl.js +1 -1
  17. package/dist/replay/ReplayStageImpl.js.map +1 -1
  18. package/dist/replay/demo/index.d.ts +3 -0
  19. package/dist/replay/demo/index.d.ts.map +1 -0
  20. package/dist/replay/demo/index.js +66 -0
  21. package/dist/replay/demo/index.js.map +1 -0
  22. package/dist/replay/index.d.ts +2 -2
  23. package/dist/replay/index.d.ts.map +1 -1
  24. package/dist/replay/index.js +1 -1
  25. package/dist/replay/index.js.map +1 -1
  26. package/dist/replay/types.js +3 -3
  27. package/dist/replay/types.js.map +1 -1
  28. package/dist/styles.css +565 -75
  29. package/package.json +8 -6
  30. package/src/index.ts +2 -1
  31. package/src/replay/ReplayPlayer.tsx +430 -0
  32. package/src/replay/ReplayPreview.tsx +111 -85
  33. package/src/replay/ReplayStage.tsx +1 -1
  34. package/src/replay/ReplayStageImpl.tsx +1 -1
  35. package/src/replay/demo/index.ts +67 -0
  36. package/src/replay/index.ts +6 -1
  37. package/src/replay/types.ts +3 -3
  38. package/src/styles.css +565 -75
  39. package/dist/replay/NativeReplay.d.ts +0 -9
  40. package/dist/replay/NativeReplay.d.ts.map +0 -1
  41. package/dist/replay/NativeReplay.js +0 -175
  42. package/dist/replay/NativeReplay.js.map +0 -1
  43. package/src/replay/NativeReplay.tsx +0 -240
@@ -1,7 +1,12 @@
1
- import { Suspense, lazy, useEffect, useState } from 'react';
1
+ import { useEffect, useState, type CSSProperties, type ReactNode } from 'react';
2
+ import { BrowserBar, ReplayPlayer } from './ReplayPlayer';
3
+ import type { ReplayPlayerOptions, ReplayPreviewHandle } from './ReplayPlayer';
2
4
  import type { ReplayEvent } from './types';
3
5
  import { normalizeReplayEmbedUrl } from './url';
4
6
 
7
+ export { formatReplayTime } from './ReplayPlayer';
8
+ export type { ReplayChrome, ReplayPlayerOptions, ReplayPreviewHandle } from './ReplayPlayer';
9
+
5
10
  export interface ReplayPageSource {
6
11
  kind: 'url';
7
12
  url: string;
@@ -26,21 +31,25 @@ export type ReplaySource =
26
31
  | ReplayEventsSource
27
32
  | ReplayEventsUrlSource;
28
33
 
29
- export interface ReplayPreviewProps {
30
- /** A public Loop QA URL, a public event endpoint, or an in-memory rrweb event list. */
31
- source: ReplaySource;
32
- /** Avoid mounting the rrweb player until the editor block is visible. */
34
+ interface ReplayPreviewCommon extends ReplayPlayerOptions {
35
+ /** Keep the player unmounted (a placeholder frame) until true, e.g. until an editor block is visible. */
33
36
  active?: boolean;
34
- /** Public-page embeds default to lazy loading; native rrweb playback defaults to paused. */
35
- autoplay?: boolean;
36
- loop?: boolean;
37
- className?: string;
38
- title?: string;
39
- /** Playback speed options mirror the Loop QA player. */
40
- speeds?: number[];
41
37
  }
42
38
 
43
- const DEFAULT_SPEEDS = [0.5, 1, 2, 4, 8];
39
+ /** Play an rrweb recording you already hold. */
40
+ export interface ReplayPreviewEventsProps extends ReplayPreviewCommon {
41
+ events: ReplayEvent[];
42
+ source?: undefined;
43
+ }
44
+
45
+ /** Embed a public Loop QA URL, a public event endpoint, or an in-memory rrweb event list. */
46
+ export interface ReplayPreviewSourceProps extends ReplayPreviewCommon {
47
+ source: ReplaySource;
48
+ events?: undefined;
49
+ }
50
+
51
+ /** Pass `events` (the player API) or `source` (the embed API): both render the same player. */
52
+ export type ReplayPreviewProps = ReplayPreviewEventsProps | ReplayPreviewSourceProps;
44
53
 
45
54
  function sourcePage(source: ReplaySource): ReplayPageSource | null {
46
55
  if (typeof source === 'string') return { kind: 'url', url: source };
@@ -59,50 +68,64 @@ function sourceEventsUrl(source: ReplaySource): ReplayEventsUrlSource | null {
59
68
  return source.kind === 'events-url' ? source : null;
60
69
  }
61
70
 
62
- function ReplayFrame({ source, title }: { source: ReplayPageSource; title?: string }) {
63
- const url = normalizeReplayEmbedUrl(source.url);
64
- if (!url) {
65
- return (
66
- <a className="replay-preview__invalid" href={source.url} target="_blank" rel="noreferrer">
67
- Open replay
68
- </a>
69
- );
70
- }
71
+ /** The frame around a non-player body (an iframe, a loading or error note): same root, same chrome hooks. */
72
+ function Frame({
73
+ kind, chrome, className, title, href, trafficLights = true, ar, style, children,
74
+ }: {
75
+ kind: string;
76
+ chrome: NonNullable<ReplayPlayerOptions['chrome']>;
77
+ className?: string;
78
+ title?: string;
79
+ href?: string;
80
+ trafficLights?: boolean;
81
+ ar?: string;
82
+ style?: CSSProperties;
83
+ children: ReactNode;
84
+ }) {
71
85
  return (
72
- <iframe
73
- className="replay-preview__frame"
74
- src={url}
75
- title={title ?? source.title ?? 'Replay preview'}
76
- loading="lazy"
77
- allow="autoplay; fullscreen"
78
- allowFullScreen
79
- referrerPolicy="strict-origin-when-cross-origin"
80
- />
86
+ <div
87
+ className={className ? `replay-preview ${className}` : 'replay-preview'}
88
+ data-slot="replay-preview"
89
+ data-replay-preview={kind}
90
+ data-chrome={chrome}
91
+ data-fit="width"
92
+ style={style}
93
+ {...(title ? { role: 'group', 'aria-label': title } : null)}
94
+ >
95
+ {chrome === 'browser' ? <BrowserBar href={href ?? ''} trafficLights={trafficLights} loading={false} /> : null}
96
+ <div data-slot="replay-preview-stage" style={ar ? ({ '--replay-ar': ar } as CSSProperties) : undefined}>
97
+ {children}
98
+ </div>
99
+ </div>
81
100
  );
82
101
  }
83
102
 
84
- // rrweb (and its stylesheet) load on first use of an event-based source.
85
- const LazyNativeReplay = lazy(() => import('./NativeReplay'));
86
-
87
- function NativeReplay(props: { events: ReplayEvent[]; autoplay?: boolean; loop?: boolean; speeds?: number[] }) {
103
+ function ReplayFrame({
104
+ source, title, chrome = 'minimal', className, style, trafficLights,
105
+ }: { source: ReplayPageSource } & Pick<ReplayPlayerOptions, 'title' | 'chrome' | 'className' | 'style' | 'trafficLights'>) {
106
+ const url = normalizeReplayEmbedUrl(source.url);
88
107
  return (
89
- <Suspense fallback={<div className="replay-preview__empty">Loading replay…</div>}>
90
- <LazyNativeReplay {...props} />
91
- </Suspense>
108
+ <Frame kind="iframe" chrome={chrome} className={className} style={style} href={url ?? source.url} trafficLights={trafficLights} ar="16 / 9">
109
+ {url ? (
110
+ <iframe
111
+ className="replay-preview__frame"
112
+ src={url}
113
+ title={title ?? source.title ?? 'Replay preview'}
114
+ loading="lazy"
115
+ allow="autoplay; fullscreen"
116
+ allowFullScreen
117
+ referrerPolicy="strict-origin-when-cross-origin"
118
+ />
119
+ ) : (
120
+ <a className="replay-preview__invalid" href={source.url} target="_blank" rel="noreferrer">
121
+ Open replay
122
+ </a>
123
+ )}
124
+ </Frame>
92
125
  );
93
126
  }
94
127
 
95
- function RemoteReplay({
96
- source,
97
- autoplay,
98
- loop,
99
- speeds,
100
- }: {
101
- source: ReplayEventsUrlSource;
102
- autoplay?: boolean;
103
- loop?: boolean;
104
- speeds?: number[];
105
- }) {
128
+ function RemoteReplay({ source, options }: { source: ReplayEventsUrlSource; options: ReplayPlayerOptions }) {
106
129
  const [events, setEvents] = useState<ReplayEvent[] | null>(null);
107
130
  const [error, setError] = useState<string | null>(null);
108
131
 
@@ -130,48 +153,51 @@ function RemoteReplay({
130
153
  return () => controller.abort();
131
154
  }, [source.url, source.fetchInit]);
132
155
 
133
- if (error) return <div className="replay-preview__error" role="alert">{error}</div>;
134
- if (!events) return <div className="replay-preview__empty">Loading replay…</div>;
135
- return <NativeReplay events={events} autoplay={autoplay} loop={loop} speeds={speeds} />;
156
+ if (events) return <ReplayPlayer {...options} events={events} />;
157
+ return (
158
+ <Frame kind="rrweb" chrome={options.chrome ?? 'browser'} className={options.className} style={options.style} href={options.url} trafficLights={options.trafficLights} ar="16 / 9">
159
+ {error ? (
160
+ <div className="replay-preview__error" role="alert">{error}</div>
161
+ ) : (
162
+ <div className="replay-preview__empty">Loading replay…</div>
163
+ )}
164
+ </Frame>
165
+ );
136
166
  }
137
167
 
138
- export function ReplayPreview({
139
- source,
140
- active = true,
141
- autoplay = false,
142
- loop = false,
143
- className,
144
- title,
145
- speeds = DEFAULT_SPEEDS,
146
- }: ReplayPreviewProps) {
147
- if (!active) return <div className={`replay-preview ${className ?? ''}`.trim()} data-replay-preview="paused" />;
148
-
149
- const page = sourcePage(source);
150
- const events = sourceEvents(source);
151
- const eventsUrl = sourceEventsUrl(source);
152
- const rootClass = `replay-preview ${className ?? ''}`.trim();
153
-
154
- if (page) {
155
- return (
156
- <div className={rootClass} data-replay-preview="iframe">
157
- <ReplayFrame source={page} title={title} />
158
- </div>
159
- );
160
- }
161
- if (events) {
168
+ /**
169
+ * A session replay in a browser frame. Pass `events` (an rrweb recording) to play it with the full player:
170
+ * URL bar, poster, scrubber with markers, pointer overlay, keyboard map, fullscreen. Or pass `source` (a
171
+ * public Loop QA URL, an events URL, or an event list), which renders the same player, or an iframe for
172
+ * page URLs. rrweb loads on first render of an event player, never for iframes.
173
+ */
174
+ export function ReplayPreview(props: ReplayPreviewProps) {
175
+ const { active = true, source, events, ...options } = props;
176
+ const { chrome, className, style } = options;
177
+
178
+ if (!active) {
162
179
  return (
163
- <div className={rootClass} data-replay-preview="rrweb">
164
- <NativeReplay events={events.events} autoplay={autoplay} loop={loop} speeds={speeds} />
165
- </div>
180
+ <div
181
+ className={className ? `replay-preview ${className}` : 'replay-preview'}
182
+ data-slot="replay-preview"
183
+ data-replay-preview="paused"
184
+ data-chrome={chrome ?? 'minimal'}
185
+ style={style}
186
+ />
166
187
  );
167
188
  }
168
- if (eventsUrl) {
169
- return (
170
- <div className={rootClass} data-replay-preview="rrweb">
171
- <RemoteReplay source={eventsUrl} autoplay={autoplay} loop={loop} speeds={speeds} />
172
- </div>
173
- );
189
+
190
+ if (events) return <ReplayPlayer {...options} events={events} />;
191
+ if (source === undefined) return null;
192
+
193
+ const page = sourcePage(source);
194
+ if (page) {
195
+ return <ReplayFrame source={page} title={options.title} chrome={chrome} className={className} style={style} trafficLights={options.trafficLights} />;
174
196
  }
197
+ const list = sourceEvents(source);
198
+ if (list) return <ReplayPlayer {...options} events={list.events} />;
199
+ const remote = sourceEventsUrl(source);
200
+ if (remote) return <RemoteReplay source={remote} options={options} />;
175
201
  return null;
176
202
  }
177
203
 
@@ -14,7 +14,7 @@ export function preloadReplayStage(): Promise<unknown> {
14
14
  /**
15
15
  * A headless rrweb player: the recorded page, fitted to its box, with no controls. Drive it
16
16
  * through the ref (`play`, `pause`, `seek`, …) and the `onTimeUpdate` / `onPlayingChange`
17
- * callbacks to build custom player chrome. Requires the optional `rrweb` peer.
17
+ * callbacks to build custom player chrome.
18
18
  */
19
19
  export const ReplayStage = forwardRef<ReplayStageHandle, ReplayStageProps>(function ReplayStage(
20
20
  { fallback = null, ...props },
@@ -1,6 +1,6 @@
1
1
  /* The headless rrweb stage: mounts rrweb's Replayer, fits the recorded viewport into its box
2
2
  and exposes an imperative handle, with no controls of its own. Loaded on demand through
3
- `ReplayStage` (and by `NativeReplay`), so rrweb and its stylesheet stay out of the eager graph. */
3
+ `ReplayStage`, so rrweb and its stylesheet stay out of the eager graph. */
4
4
  import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react'
5
5
  import type { CSSProperties } from 'react'
6
6
  import { Replayer } from 'rrweb'
@@ -0,0 +1,67 @@
1
+ import type { ReplayEvent } from '../types';
2
+
3
+ /* A small synthetic rrweb recording (58 events, ~9 s, 1280×800) for ReplayPreview's stories, docs and the loop-qa
4
+ block: a shopper on a mock "Northwind" storefront adds two products, opens the cart, types an email and a
5
+ promo code, and "Place order" fails — a custom `network` event (POST /api/checkout 500) and an `error` event
6
+ mark the moment. Recorded with rrweb 2.0.0-alpha.20 against a local page (no external assets), timestamps
7
+ rebased to 2026-09-24 14:32:08 UTC so every render is identical. */
8
+ export const replayDemoEvents: ReplayEvent[] = [
9
+ {"type":4,"data":{"href":"https://shop.northwind.test/collections/trail","width":1280,"height":800},"timestamp":1790260328000},
10
+ {"type":2,"data":{"node":{"type":0,"childNodes":[{"type":1,"name":"html","publicId":"","systemId":"","id":2},{"type":2,"tagName":"html","attributes":{},"childNodes":[{"type":2,"tagName":"head","attributes":{},"childNodes":[{"type":2,"tagName":"meta","attributes":{"charset":"utf-8"},"childNodes":[],"id":5},{"type":2,"tagName":"title","attributes":{},"childNodes":[{"type":3,"textContent":"Northwind Outfitters","id":7}],"id":6},{"type":3,"textContent":"\n","id":8},{"type":2,"tagName":"style","attributes":{"_cssText":"* { box-sizing: border-box; }body { margin: 0px; font: 14px / 1.45 -apple-system, BlinkMacSystemFont, \"Segoe UI\", Inter, sans-serif; color: rgb(28, 25, 23); background: rgb(250, 250, 249); }header { display: flex; align-items: center; gap: 28px; height: 64px; padding: 0px 40px; background: rgb(255, 255, 255); border-bottom: 1px solid rgb(231, 229, 228); }.logo { font-weight: 700; font-size: 17px; letter-spacing: -0.02em; }.logo b { color: rgb(13, 148, 136); }nav a { color: rgb(87, 83, 78); text-decoration: none; margin-right: 20px; }.sp { flex: 1 1 0%; }.cart { position: relative; border: 1px solid rgb(214, 211, 209); background: rgb(255, 255, 255); border-radius: 999px; padding: 7px 14px; font: inherit; cursor: pointer; }.badge { position: absolute; top: -6px; right: -6px; min-width: 18px; height: 18px; border-radius: 9px; background: rgb(13, 148, 136); color: rgb(255, 255, 255); font-size: 11px; display: grid; place-items: center; padding: 0px 5px; }.hero { padding: 36px 40px 12px; }.hero h1 { margin: 0px; font-size: 30px; letter-spacing: -0.03em; }.hero p { margin: 6px 0px 0px; color: rgb(120, 113, 108); }.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; padding: 20px 40px; }.card { background: rgb(255, 255, 255); border: 1px solid rgb(231, 229, 228); border-radius: 14px; overflow: hidden; transition: box-shadow 0.2s; }.card:hover { box-shadow: rgba(0, 0, 0, 0.08) 0px 8px 24px; }.img { height: 150px; }.i1 { background: linear-gradient(135deg, rgb(153, 246, 228), rgb(13, 148, 136)); }.i2 { background: linear-gradient(135deg, rgb(253, 230, 138), rgb(217, 119, 6)); }.i3 { background: linear-gradient(135deg, rgb(199, 210, 254), rgb(79, 70, 229)); }.i4 { background: linear-gradient(135deg, rgb(254, 205, 211), rgb(225, 29, 72)); }.body { padding: 12px 14px 14px; }.name { font-weight: 600; }.price { color: rgb(120, 113, 108); margin: 2px 0px 10px; }.add { width: 100%; border: 0px; border-radius: 9px; background: rgb(28, 25, 23); color: rgb(255, 255, 255); padding: 9px; font-style: inherit; font-variant: inherit; font-stretch: inherit; font-size: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-weight: 600; cursor: pointer; }.drawer { position: fixed; top: 0px; right: 0px; bottom: 0px; width: 380px; background: rgb(255, 255, 255); border-left: 1px solid rgb(231, 229, 228); box-shadow: rgba(0, 0, 0, 0.12) -12px 0px 40px; padding: 24px; transform: translateX(100%); transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1); }.drawer.open { transform: none; }.drawer h2 { margin: 0px 0px 16px; font-size: 18px; }.line { display: flex; justify-content: space-between; padding: 10px 0px; border-bottom: 1px solid rgb(245, 245, 244); }label { display: block; font-size: 12px; color: rgb(120, 113, 108); margin: 14px 0px 4px; }input { width: 100%; border: 1px solid rgb(214, 211, 209); border-radius: 9px; padding: 9px 11px; font: inherit; }.pay { margin-top: 18px; width: 100%; border: 0px; border-radius: 10px; background: rgb(13, 148, 136); color: rgb(255, 255, 255); padding: 11px; font-style: inherit; font-variant: inherit; font-stretch: inherit; font-size: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-language-override: inherit; font-weight: 600; cursor: pointer; }.err { display: none; margin-top: 14px; border-radius: 10px; background: rgb(254, 242, 242); border: 1px solid rgb(254, 202, 202); color: rgb(185, 28, 28); padding: 10px 12px; font-size: 13px; }.err.show { display: block; }"},"childNodes":[{"type":3,"textContent":"","id":10}],"id":9},{"type":2,"tagName":"script","attributes":{"type":"text/javascript"},"childNodes":[{"type":3,"textContent":"SCRIPT_PLACEHOLDER","id":12}],"id":11}],"id":4},{"type":2,"tagName":"body","attributes":{},"childNodes":[{"type":3,"textContent":"\n","id":14},{"type":2,"tagName":"header","attributes":{},"childNodes":[{"type":2,"tagName":"div","attributes":{"class":"logo"},"childNodes":[{"type":3,"textContent":"north","id":17},{"type":2,"tagName":"b","attributes":{},"childNodes":[{"type":3,"textContent":"wind","id":19}],"id":18}],"id":16},{"type":2,"tagName":"nav","attributes":{},"childNodes":[{"type":2,"tagName":"a","attributes":{"href":"file:///tmp/rr/page.html#"},"childNodes":[{"type":3,"textContent":"New","id":22}],"id":21},{"type":2,"tagName":"a","attributes":{"href":"file:///tmp/rr/page.html#"},"childNodes":[{"type":3,"textContent":"Trail","id":24}],"id":23},{"type":2,"tagName":"a","attributes":{"href":"file:///tmp/rr/page.html#"},"childNodes":[{"type":3,"textContent":"Road","id":26}],"id":25},{"type":2,"tagName":"a","attributes":{"href":"file:///tmp/rr/page.html#"},"childNodes":[{"type":3,"textContent":"Sale","id":28}],"id":27}],"id":20},{"type":2,"tagName":"div","attributes":{"class":"sp"},"childNodes":[],"id":29},{"type":2,"tagName":"button","attributes":{"class":"cart","id":"cart"},"childNodes":[{"type":3,"textContent":"Cart ","id":31},{"type":2,"tagName":"span","attributes":{"class":"badge","id":"count"},"childNodes":[{"type":3,"textContent":"0","id":33}],"id":32}],"id":30}],"id":15},{"type":3,"textContent":"\n","id":34},{"type":2,"tagName":"section","attributes":{"class":"hero"},"childNodes":[{"type":2,"tagName":"h1","attributes":{},"childNodes":[{"type":3,"textContent":"Fall trail collection","id":37}],"id":36},{"type":2,"tagName":"p","attributes":{},"childNodes":[{"type":3,"textContent":"Built for mud, rain and long miles.","id":39}],"id":38}],"id":35},{"type":3,"textContent":"\n","id":40},{"type":2,"tagName":"section","attributes":{"class":"grid"},"childNodes":[{"type":3,"textContent":"\n","id":42},{"type":2,"tagName":"div","attributes":{"class":"card"},"childNodes":[{"type":2,"tagName":"div","attributes":{"class":"img i1"},"childNodes":[],"id":44},{"type":2,"tagName":"div","attributes":{"class":"body"},"childNodes":[{"type":2,"tagName":"div","attributes":{"class":"name"},"childNodes":[{"type":3,"textContent":"Trail Runner 2","id":47}],"id":46},{"type":2,"tagName":"div","attributes":{"class":"price"},"childNodes":[{"type":3,"textContent":"$148","id":49}],"id":48},{"type":2,"tagName":"button","attributes":{"class":"add","data-p":"Trail Runner 2","data-c":"148"},"childNodes":[{"type":3,"textContent":"Add to cart","id":51}],"id":50}],"id":45}],"id":43},{"type":3,"textContent":"\n","id":52},{"type":2,"tagName":"div","attributes":{"class":"card"},"childNodes":[{"type":2,"tagName":"div","attributes":{"class":"img i2"},"childNodes":[],"id":54},{"type":2,"tagName":"div","attributes":{"class":"body"},"childNodes":[{"type":2,"tagName":"div","attributes":{"class":"name"},"childNodes":[{"type":3,"textContent":"Summit Shell","id":57}],"id":56},{"type":2,"tagName":"div","attributes":{"class":"price"},"childNodes":[{"type":3,"textContent":"$229","id":59}],"id":58},{"type":2,"tagName":"button","attributes":{"class":"add","data-p":"Summit Shell","data-c":"229"},"childNodes":[{"type":3,"textContent":"Add to cart","id":61}],"id":60}],"id":55}],"id":53},{"type":3,"textContent":"\n","id":62},{"type":2,"tagName":"div","attributes":{"class":"card"},"childNodes":[{"type":2,"tagName":"div","attributes":{"class":"img i3"},"childNodes":[],"id":64},{"type":2,"tagName":"div","attributes":{"class":"body"},"childNodes":[{"type":2,"tagName":"div","attributes":{"class":"name"},"childNodes":[{"type":3,"textContent":"Ridge Pack 28L","id":67}],"id":66},{"type":2,"tagName":"div","attributes":{"class":"price"},"childNodes":[{"type":3,"textContent":"$119","id":69}],"id":68},{"type":2,"tagName":"button","attributes":{"class":"add","data-p":"Ridge Pack 28L","data-c":"119"},"childNodes":[{"type":3,"textContent":"Add to cart","id":71}],"id":70}],"id":65}],"id":63},{"type":3,"textContent":"\n","id":72},{"type":2,"tagName":"div","attributes":{"class":"card"},"childNodes":[{"type":2,"tagName":"div","attributes":{"class":"img i4"},"childNodes":[],"id":74},{"type":2,"tagName":"div","attributes":{"class":"body"},"childNodes":[{"type":2,"tagName":"div","attributes":{"class":"name"},"childNodes":[{"type":3,"textContent":"Merino Crew","id":77}],"id":76},{"type":2,"tagName":"div","attributes":{"class":"price"},"childNodes":[{"type":3,"textContent":"$68","id":79}],"id":78},{"type":2,"tagName":"button","attributes":{"class":"add","data-p":"Merino Crew","data-c":"68"},"childNodes":[{"type":3,"textContent":"Add to cart","id":81}],"id":80}],"id":75}],"id":73},{"type":3,"textContent":"\n","id":82}],"id":41},{"type":3,"textContent":"\n","id":83},{"type":2,"tagName":"aside","attributes":{"class":"drawer","id":"drawer"},"childNodes":[{"type":2,"tagName":"h2","attributes":{},"childNodes":[{"type":3,"textContent":"Your cart","id":86}],"id":85},{"type":2,"tagName":"div","attributes":{"id":"lines"},"childNodes":[],"id":87},{"type":2,"tagName":"div","attributes":{"class":"line"},"childNodes":[{"type":2,"tagName":"b","attributes":{},"childNodes":[{"type":3,"textContent":"Total","id":90}],"id":89},{"type":2,"tagName":"b","attributes":{"id":"total"},"childNodes":[{"type":3,"textContent":"$0","id":92}],"id":91}],"id":88},{"type":3,"textContent":"\n","id":93},{"type":2,"tagName":"label","attributes":{},"childNodes":[{"type":3,"textContent":"Email","id":95}],"id":94},{"type":2,"tagName":"input","attributes":{"id":"email","placeholder":"you@example.com"},"childNodes":[],"id":96},{"type":2,"tagName":"label","attributes":{},"childNodes":[{"type":3,"textContent":"Promo code","id":98}],"id":97},{"type":2,"tagName":"input","attributes":{"id":"promo","placeholder":"Optional"},"childNodes":[],"id":99},{"type":3,"textContent":"\n","id":100},{"type":2,"tagName":"button","attributes":{"class":"pay","id":"pay"},"childNodes":[{"type":3,"textContent":"Place order","id":102}],"id":101},{"type":2,"tagName":"div","attributes":{"class":"err","id":"err"},"childNodes":[{"type":3,"textContent":"Something went wrong. Please try again.","id":104}],"id":103}],"id":84},{"type":3,"textContent":"\n","id":105},{"type":2,"tagName":"script","attributes":{},"childNodes":[{"type":3,"textContent":"SCRIPT_PLACEHOLDER","id":107}],"id":106},{"type":3,"textContent":"\n","id":108}],"id":13}],"id":3}],"id":1},"initialOffset":{"left":0,"top":0}},"timestamp":1790260328004},
11
+ {"type":3,"data":{"source":1,"positions":[{"x":640,"y":300,"id":41,"timeOffset":0}]},"timestamp":1790260328010},
12
+ {"type":3,"data":{"source":1,"positions":[{"x":608,"y":302,"id":54,"timeOffset":-99},{"x":357,"y":319,"id":54,"timeOffset":-35}]},"timestamp":1790260328511},
13
+ {"type":3,"data":{"source":1,"positions":[{"x":223,"y":330,"id":44,"timeOffset":0}]},"timestamp":1790260329021},
14
+ {"type":3,"data":{"source":1,"positions":[{"x":410,"y":330,"id":54,"timeOffset":-438},{"x":454,"y":341,"id":54,"timeOffset":-109},{"x":247,"y":433,"id":50,"timeOffset":-46}]},"timestamp":1790260329522},
15
+ {"type":3,"data":{"source":2,"type":1,"id":50,"x":182.5,"y":436.5299987792969},"timestamp":1790260329770},
16
+ {"type":3,"data":{"source":2,"type":5,"id":50},"timestamp":1790260329770},
17
+ {"type":3,"data":{"source":2,"type":0,"id":50,"x":182.5,"y":436.5299987792969},"timestamp":1790260329770},
18
+ {"type":3,"data":{"source":2,"type":2,"id":50,"x":182,"y":436,"pointerType":0},"timestamp":1790260329770},
19
+ {"type":3,"data":{"source":0,"texts":[],"attributes":[],"removes":[{"parentId":32,"id":33},{"parentId":91,"id":92}],"adds":[{"parentId":32,"nextId":null,"node":{"type":3,"textContent":"1","id":109}},{"parentId":87,"nextId":null,"node":{"type":2,"tagName":"div","attributes":{"class":"line"},"childNodes":[],"id":110}},{"parentId":110,"nextId":null,"node":{"type":2,"tagName":"span","attributes":{},"childNodes":[],"id":111}},{"parentId":111,"nextId":null,"node":{"type":3,"textContent":"$148","id":112}},{"parentId":91,"nextId":null,"node":{"type":3,"textContent":"$148","id":113}},{"parentId":110,"nextId":111,"node":{"type":2,"tagName":"span","attributes":{},"childNodes":[],"id":114}},{"parentId":114,"nextId":null,"node":{"type":3,"textContent":"Trail Runner 2","id":115}}]},"timestamp":1790260329771},
20
+ {"type":3,"data":{"source":1,"positions":[{"x":182,"y":436,"id":50,"timeOffset":-253}]},"timestamp":1790260330023},
21
+ {"type":5,"data":{"tag":"checkpoint","payload":{"label":"Added Trail Runner 2"}},"timestamp":1790260330279},
22
+ {"type":3,"data":{"source":1,"positions":[{"x":223,"y":438,"id":50,"timeOffset":-243},{"x":512,"y":454,"id":60,"timeOffset":-181}]},"timestamp":1790260330524},
23
+ {"type":3,"data":{"source":2,"type":1,"id":70,"x":792.5,"y":436.5299987792969},"timestamp":1790260330663},
24
+ {"type":3,"data":{"source":2,"type":6,"id":50},"timestamp":1790260330663},
25
+ {"type":3,"data":{"source":2,"type":5,"id":70},"timestamp":1790260330663},
26
+ {"type":3,"data":{"source":2,"type":0,"id":70,"x":792.5,"y":436.5299987792969},"timestamp":1790260330663},
27
+ {"type":3,"data":{"source":2,"type":2,"id":70,"x":792,"y":436,"pointerType":0},"timestamp":1790260330663},
28
+ {"type":3,"data":{"source":0,"texts":[],"attributes":[],"removes":[{"parentId":32,"id":109},{"parentId":87,"id":110},{"parentId":91,"id":113}],"adds":[{"parentId":32,"nextId":null,"node":{"type":3,"textContent":"2","id":116}},{"parentId":87,"nextId":null,"node":{"type":2,"tagName":"div","attributes":{"class":"line"},"childNodes":[],"id":117}},{"parentId":117,"nextId":null,"node":{"type":2,"tagName":"span","attributes":{},"childNodes":[],"id":118}},{"parentId":118,"nextId":null,"node":{"type":3,"textContent":"$119","id":119}},{"parentId":91,"nextId":null,"node":{"type":3,"textContent":"$267","id":120}},{"parentId":87,"nextId":117,"node":{"type":2,"tagName":"div","attributes":{"class":"line"},"childNodes":[],"id":121}},{"parentId":121,"nextId":null,"node":{"type":2,"tagName":"span","attributes":{},"childNodes":[],"id":122}},{"parentId":121,"nextId":122,"node":{"type":2,"tagName":"span","attributes":{},"childNodes":[],"id":123}},{"parentId":123,"nextId":null,"node":{"type":3,"textContent":"Trail Runner 2","id":124}},{"parentId":122,"nextId":null,"node":{"type":3,"textContent":"$148","id":125}},{"parentId":117,"nextId":118,"node":{"type":2,"tagName":"span","attributes":{},"childNodes":[],"id":126}},{"parentId":126,"nextId":null,"node":{"type":3,"textContent":"Ridge Pack 28L","id":127}}]},"timestamp":1790260330664},
29
+ {"type":3,"data":{"source":1,"positions":[{"x":792,"y":436,"id":70,"timeOffset":-363}]},"timestamp":1790260331025},
30
+ {"type":3,"data":{"source":1,"positions":[{"x":815,"y":414,"id":65,"timeOffset":-249},{"x":1003,"y":234,"id":74,"timeOffset":-183},{"x":1191,"y":54,"id":15,"timeOffset":-116}]},"timestamp":1790260331526},
31
+ {"type":3,"data":{"source":2,"type":1,"id":30,"x":1211.1400146484375,"y":31.489999771118164},"timestamp":1790260331688},
32
+ {"type":3,"data":{"source":2,"type":6,"id":70},"timestamp":1790260331688},
33
+ {"type":3,"data":{"source":2,"type":5,"id":30},"timestamp":1790260331688},
34
+ {"type":3,"data":{"source":2,"type":0,"id":30,"x":1211.1400146484375,"y":31.489999771118164},"timestamp":1790260331688},
35
+ {"type":3,"data":{"source":2,"type":2,"id":30,"x":1211,"y":31,"pointerType":0},"timestamp":1790260331688},
36
+ {"type":3,"data":{"source":0,"texts":[],"attributes":[{"id":84,"attributes":{"class":"drawer open"}}],"removes":[],"adds":[]},"timestamp":1790260331688},
37
+ {"type":3,"data":{"source":1,"positions":[{"x":1211,"y":31,"id":30,"timeOffset":-339}]},"timestamp":1790260332028},
38
+ {"type":3,"data":{"source":1,"positions":[{"x":1200,"y":53,"id":84,"timeOffset":-137},{"x":1112,"y":225,"id":96,"timeOffset":-68}]},"timestamp":1790260332529},
39
+ {"type":3,"data":{"source":2,"type":1,"id":96,"x":1090.5,"y":245.52000427246094},"timestamp":1790260332705},
40
+ {"type":3,"data":{"source":2,"type":6,"id":30},"timestamp":1790260332705},
41
+ {"type":3,"data":{"source":2,"type":5,"id":96},"timestamp":1790260332705},
42
+ {"type":3,"data":{"source":2,"type":0,"id":96,"x":1090.5,"y":245.52000427246094},"timestamp":1790260332705},
43
+ {"type":3,"data":{"source":2,"type":2,"id":96,"x":1090,"y":245,"pointerType":0},"timestamp":1790260332705},
44
+ {"type":3,"data":{"source":1,"positions":[{"x":1090,"y":245,"id":96,"timeOffset":-325}]},"timestamp":1790260333030},
45
+ {"type":3,"data":{"source":1,"positions":[{"x":1088,"y":263,"id":96,"timeOffset":0}]},"timestamp":1790260333853},
46
+ {"type":3,"data":{"source":2,"type":1,"id":99,"x":1090.5,"y":321.2099914550781},"timestamp":1790260333913},
47
+ {"type":3,"data":{"source":5,"text":"maya@northwind.test","isChecked":false,"id":96},"timestamp":1790260333913},
48
+ {"type":3,"data":{"source":2,"type":6,"id":96},"timestamp":1790260333913},
49
+ {"type":3,"data":{"source":2,"type":5,"id":99},"timestamp":1790260333913},
50
+ {"type":3,"data":{"source":2,"type":0,"id":99,"x":1090.5,"y":321.2099914550781},"timestamp":1790260333914},
51
+ {"type":3,"data":{"source":2,"type":2,"id":99,"x":1090,"y":321,"pointerType":0},"timestamp":1790260333914},
52
+ {"type":3,"data":{"source":1,"positions":[{"x":1090,"y":321,"id":99,"timeOffset":-441}]},"timestamp":1790260334354},
53
+ {"type":3,"data":{"source":1,"positions":[{"x":1090,"y":330,"id":99,"timeOffset":-193},{"x":1090,"y":401,"id":101,"timeOffset":-128}]},"timestamp":1790260334855},
54
+ {"type":3,"data":{"source":2,"type":1,"id":101,"x":1090.5,"y":380.5},"timestamp":1790260334954},
55
+ {"type":3,"data":{"source":5,"text":"FALL25","isChecked":false,"id":99},"timestamp":1790260334954},
56
+ {"type":3,"data":{"source":2,"type":6,"id":99},"timestamp":1790260334954},
57
+ {"type":3,"data":{"source":2,"type":5,"id":101},"timestamp":1790260334954},
58
+ {"type":3,"data":{"source":2,"type":0,"id":101,"x":1090.5,"y":380.5},"timestamp":1790260334954},
59
+ {"type":3,"data":{"source":2,"type":2,"id":101,"x":1090,"y":380,"pointerType":0},"timestamp":1790260334954},
60
+ {"type":3,"data":{"source":0,"texts":[],"attributes":[],"removes":[{"parentId":101,"id":102}],"adds":[{"parentId":101,"nextId":null,"node":{"type":3,"textContent":"Placing order…","id":128}}]},"timestamp":1790260334954},
61
+ {"type":3,"data":{"source":1,"positions":[{"x":1090,"y":380,"id":101,"timeOffset":-402}]},"timestamp":1790260335356},
62
+ {"type":5,"data":{"tag":"network","payload":{"method":"POST","status":500,"url":"/api/checkout","label":"POST /api/checkout 500"}},"timestamp":1790260335855},
63
+ {"type":5,"data":{"tag":"error","payload":{"label":"TypeError","message":"Cannot read properties of undefined (reading 'total') at applyPromo (checkout.ts:88)"}},"timestamp":1790260335856},
64
+ {"type":3,"data":{"source":0,"texts":[],"attributes":[{"id":103,"attributes":{"class":"err show"}}],"removes":[{"parentId":101,"id":128}],"adds":[{"parentId":101,"nextId":null,"node":{"type":3,"textContent":"Place order","id":129}}]},"timestamp":1790260335856},
65
+ {"type":3,"data":{"source":1,"positions":[{"x":1085,"y":388,"id":101,"timeOffset":0}]},"timestamp":1790260336357},
66
+ {"type":3,"data":{"source":1,"positions":[{"x":1050,"y":444,"id":103,"timeOffset":-441}]},"timestamp":1790260336859},
67
+ ];
@@ -1,9 +1,14 @@
1
- export { ReplayEmbed, ReplayPreview } from './ReplayPreview';
1
+ export { ReplayEmbed, ReplayPreview, formatReplayTime } from './ReplayPreview';
2
2
  export type {
3
+ ReplayChrome,
3
4
  ReplayEventsSource,
4
5
  ReplayEventsUrlSource,
5
6
  ReplayPageSource,
7
+ ReplayPlayerOptions,
8
+ ReplayPreviewEventsProps,
9
+ ReplayPreviewHandle,
6
10
  ReplayPreviewProps,
11
+ ReplayPreviewSourceProps,
7
12
  ReplaySource,
8
13
  } from './ReplayPreview';
9
14
  export { ReplayStage, preloadReplayStage } from './ReplayStage';
@@ -1,7 +1,7 @@
1
1
  /* rrweb's event shape and the numeric enums the replay helpers read, declared locally so the
2
- eager replay modules (and their published declarations) never import rrweb. rrweb is an
3
- optional peer: only the lazily loaded player chunk (`ReplayStageImpl` / `NativeReplay`)
4
- imports it, so hosts that never play native events don't need it installed. */
2
+ eager replay modules (and their published declarations) never import rrweb. rrweb is
3
+ lazy: only the player chunk (`ReplayStageImpl`) imports it, so pages that
4
+ never play native events never download it. */
5
5
 
6
6
  /** One recorded rrweb event. rrweb's own `eventWithTime` is assignable to it. */
7
7
  export interface ReplayEvent {