@brett_lamy/docstream 1.6.1 → 1.8.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/README.md +31 -4
- package/dist/demo/DemoViewer.d.ts +8 -0
- package/dist/demo/DemoViewer.d.ts.map +1 -1
- package/dist/demo/DemoViewer.js +74 -1
- package/dist/demo/DemoViewer.js.map +1 -1
- package/dist/demo/PathBreadcrumb.d.ts +54 -0
- package/dist/demo/PathBreadcrumb.d.ts.map +1 -0
- package/dist/demo/PathBreadcrumb.js +245 -0
- package/dist/demo/PathBreadcrumb.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/replay/ReplayPlayer.d.ts +64 -0
- package/dist/replay/ReplayPlayer.d.ts.map +1 -0
- package/dist/replay/ReplayPlayer.js +211 -0
- package/dist/replay/ReplayPlayer.js.map +1 -0
- package/dist/replay/ReplayPreview.d.ts +24 -12
- package/dist/replay/ReplayPreview.d.ts.map +1 -1
- package/dist/replay/ReplayPreview.js +37 -32
- package/dist/replay/ReplayPreview.js.map +1 -1
- package/dist/replay/ReplayStageImpl.js +1 -1
- package/dist/replay/ReplayStageImpl.js.map +1 -1
- package/dist/replay/demo/index.d.ts +3 -0
- package/dist/replay/demo/index.d.ts.map +1 -0
- package/dist/replay/demo/index.js +66 -0
- package/dist/replay/demo/index.js.map +1 -0
- package/dist/replay/index.d.ts +2 -2
- package/dist/replay/index.d.ts.map +1 -1
- package/dist/replay/index.js +1 -1
- package/dist/replay/index.js.map +1 -1
- package/dist/replay/types.js +1 -1
- package/dist/styles.css +791 -79
- package/package.json +7 -1
- package/src/demo/DemoViewer.tsx +94 -2
- package/src/demo/PathBreadcrumb.tsx +367 -0
- package/src/index.ts +2 -1
- package/src/replay/ReplayPlayer.tsx +430 -0
- package/src/replay/ReplayPreview.tsx +111 -85
- package/src/replay/ReplayStageImpl.tsx +1 -1
- package/src/replay/demo/index.ts +67 -0
- package/src/replay/index.ts +6 -1
- package/src/replay/types.ts +1 -1
- package/src/styles.css +791 -79
- package/dist/replay/NativeReplay.d.ts +0 -9
- package/dist/replay/NativeReplay.d.ts.map +0 -1
- package/dist/replay/NativeReplay.js +0 -175
- package/dist/replay/NativeReplay.js.map +0 -1
- package/src/replay/NativeReplay.tsx +0 -240
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* The Code view's path header: a VS Code style breadcrumb for the active file.
|
|
3
|
+
|
|
4
|
+
One button per path part (the demo `src` first, then the folders, then the file), with a chevron
|
|
5
|
+
between them. A folder button opens a menu of that folder's siblings (everything in its parent
|
|
6
|
+
folder, the current one marked); the file button lists the files beside it. Picking a file selects
|
|
7
|
+
it, picking a folder opens the first file inside it. When the segments don't fit the container the
|
|
8
|
+
middle collapses into a single `…` that lists the hidden segments.
|
|
9
|
+
|
|
10
|
+
Plain accessible DOM (nav > ol, buttons, role="menu"), so docstream needs nothing beyond React.
|
|
11
|
+
The menu is portalled and `position: fixed`, so the viewer's overflow never clips it. The segment,
|
|
12
|
+
sibling and fit maths are pure and exported (see PathBreadcrumb.test.ts). */
|
|
13
|
+
import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState, } from "react";
|
|
14
|
+
import { createPortal } from "react-dom";
|
|
15
|
+
import { Check, ChevronRight, FileCode2, Folder } from "lucide-react";
|
|
16
|
+
/** `src` parts, then the folders, then the file. */
|
|
17
|
+
export function buildCrumbs(src, filePath) {
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const name of src.split("/").filter(Boolean))
|
|
20
|
+
out.push({ key: `s${out.length}:${name}`, name, kind: "src", path: "" });
|
|
21
|
+
const parts = filePath.split("/").filter(Boolean);
|
|
22
|
+
parts.forEach((name, index) => {
|
|
23
|
+
const path = parts.slice(0, index + 1).join("/");
|
|
24
|
+
out.push({ key: `p:${path}`, name, kind: index === parts.length - 1 ? "file" : "dir", path });
|
|
25
|
+
});
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
const parentOf = (path) => path.slice(0, Math.max(0, path.lastIndexOf("/")));
|
|
29
|
+
/** The first file (in list order) at or below `folder`; `""` is the demo root. */
|
|
30
|
+
export function firstFileIn(paths, folder) {
|
|
31
|
+
return folder === "" ? paths[0] : paths.find((p) => p.startsWith(`${folder}/`));
|
|
32
|
+
}
|
|
33
|
+
/** The direct children of `folder` (`""` is the root): folders first, then files, each A to Z. */
|
|
34
|
+
export function childEntries(paths, folder) {
|
|
35
|
+
const prefix = folder === "" ? "" : `${folder}/`;
|
|
36
|
+
const dirs = new Map();
|
|
37
|
+
const files = [];
|
|
38
|
+
for (const path of paths) {
|
|
39
|
+
if (!path.startsWith(prefix))
|
|
40
|
+
continue;
|
|
41
|
+
const rest = path.slice(prefix.length);
|
|
42
|
+
const slash = rest.indexOf("/");
|
|
43
|
+
if (slash < 0)
|
|
44
|
+
files.push({ kind: "file", name: rest, path, target: path });
|
|
45
|
+
else {
|
|
46
|
+
const name = rest.slice(0, slash);
|
|
47
|
+
if (!dirs.has(name))
|
|
48
|
+
dirs.set(name, { kind: "dir", name, path: prefix + name, target: path });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const byName = (a, b) => a.name.localeCompare(b.name, undefined, { numeric: true });
|
|
52
|
+
return [...[...dirs.values()].sort(byName), ...files.sort(byName)];
|
|
53
|
+
}
|
|
54
|
+
/** The menu a segment opens: the entries beside it. A `src` segment is the demo root, so it lists the
|
|
55
|
+
* root; the file segment lists only the files in its folder. */
|
|
56
|
+
export function siblingEntries(paths, segment) {
|
|
57
|
+
if (segment.kind === "src")
|
|
58
|
+
return childEntries(paths, "");
|
|
59
|
+
const entries = childEntries(paths, parentOf(segment.path));
|
|
60
|
+
return segment.kind === "file" ? entries.filter((e) => e.kind === "file") : entries;
|
|
61
|
+
}
|
|
62
|
+
/** The menu behind `…`: the hidden segments, each opening like a folder picked in a sibling menu. */
|
|
63
|
+
export function hiddenEntries(paths, segments, range) {
|
|
64
|
+
return segments.slice(range.start, range.end).flatMap((segment) => {
|
|
65
|
+
const target = firstFileIn(paths, segment.path);
|
|
66
|
+
return target === undefined ? [] : [{ kind: segment.kind === "file" ? "file" : "dir", name: segment.name, path: segment.path, target }];
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Whether the active file is this entry, or inside this folder. */
|
|
70
|
+
export function entryContains(entry, active) {
|
|
71
|
+
return entry.path !== "" && (active === entry.path || active.startsWith(`${entry.path}/`));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Which middle segments to hide so the breadcrumb fits `available` px. `widths` are the full item
|
|
75
|
+
* widths (each includes its own chevron), `ellipsisWidth` the width of the `…` item. The first and
|
|
76
|
+
* last segments always stay; as many trailing segments as fit are kept; null when everything fits
|
|
77
|
+
* (or there is nothing to hide). With no room at all you still get first, `…`, last.
|
|
78
|
+
*/
|
|
79
|
+
export function collapseRange(widths, ellipsisWidth, available) {
|
|
80
|
+
const n = widths.length;
|
|
81
|
+
if (n < 3)
|
|
82
|
+
return null;
|
|
83
|
+
if (widths.reduce((sum, w) => sum + w, 0) <= available)
|
|
84
|
+
return null;
|
|
85
|
+
let tail = 0;
|
|
86
|
+
for (let end = n - 1; end >= 2; end--) {
|
|
87
|
+
tail += widths[end];
|
|
88
|
+
if (widths[0] + ellipsisWidth + tail > available)
|
|
89
|
+
return { start: 1, end: Math.min(n - 1, end + 1) };
|
|
90
|
+
}
|
|
91
|
+
return { start: 1, end: 2 };
|
|
92
|
+
}
|
|
93
|
+
const useIsoLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
94
|
+
export function PathBreadcrumb({ src, path, paths, onSelect, className }) {
|
|
95
|
+
const segments = buildCrumbs(src, path);
|
|
96
|
+
const nav = useRef(null);
|
|
97
|
+
const measure = useRef(null);
|
|
98
|
+
const [range, setRange] = useState(null);
|
|
99
|
+
const [menu, setMenu] = useState(null);
|
|
100
|
+
const menuId = useId();
|
|
101
|
+
const signature = segments.map((s) => s.name).join("/");
|
|
102
|
+
const fit = useCallback(() => {
|
|
103
|
+
const el = nav.current;
|
|
104
|
+
const items = measure.current ? Array.from(measure.current.children) : [];
|
|
105
|
+
if (!el || items.length < 2)
|
|
106
|
+
return;
|
|
107
|
+
const available = el.clientWidth;
|
|
108
|
+
if (!available)
|
|
109
|
+
return; // hidden (display: none) or not laid out: keep what we have
|
|
110
|
+
const widths = items.map((item) => item.getBoundingClientRect().width);
|
|
111
|
+
const ellipsis = widths.pop();
|
|
112
|
+
const next = collapseRange(widths, ellipsis, available);
|
|
113
|
+
setRange((prev) => (prev?.start === next?.start && prev?.end === next?.end ? prev : next));
|
|
114
|
+
}, []);
|
|
115
|
+
useIsoLayoutEffect(fit, [fit, signature]);
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
const el = nav.current;
|
|
118
|
+
if (!el || typeof ResizeObserver === "undefined")
|
|
119
|
+
return;
|
|
120
|
+
const observer = new ResizeObserver(fit);
|
|
121
|
+
observer.observe(el);
|
|
122
|
+
if (measure.current)
|
|
123
|
+
observer.observe(measure.current);
|
|
124
|
+
return () => observer.disconnect();
|
|
125
|
+
}, [fit]);
|
|
126
|
+
const close = useCallback((refocus) => {
|
|
127
|
+
setMenu((current) => {
|
|
128
|
+
if (current && refocus && current.anchor.isConnected)
|
|
129
|
+
current.anchor.focus();
|
|
130
|
+
return null;
|
|
131
|
+
});
|
|
132
|
+
}, []);
|
|
133
|
+
const choose = (entry) => {
|
|
134
|
+
setMenu(null);
|
|
135
|
+
onSelect(entry.target);
|
|
136
|
+
// The segments are re-keyed for the new file; park focus on its last segment.
|
|
137
|
+
requestAnimationFrame(() => nav.current?.querySelector("[data-crumb-last]")?.focus());
|
|
138
|
+
};
|
|
139
|
+
const open = (id, anchor, label, entries) => setMenu((current) => (current?.id === id ? null : { id, anchor, label, entries }));
|
|
140
|
+
const visible = range
|
|
141
|
+
? [...segments.slice(0, range.start), "…", ...segments.slice(range.end)]
|
|
142
|
+
: segments;
|
|
143
|
+
return (_jsxs("nav", { ref: nav, className: className ? `docs-demo-crumbs ${className}` : "docs-demo-crumbs", "aria-label": "Breadcrumb", children: [_jsx("ol", { className: "docs-demo-crumbs-list", children: visible.map((item, index) => {
|
|
144
|
+
const hidden = item === "…";
|
|
145
|
+
const id = hidden ? "…" : item.key;
|
|
146
|
+
const last = !hidden && item.kind === "file";
|
|
147
|
+
const expanded = menu?.id === id;
|
|
148
|
+
const label = hidden ? "Hidden path segments" : item.kind === "src" ? "Demo files" : `Files in ${parentOf(item.path) || "the demo root"}`;
|
|
149
|
+
const entries = () => (hidden ? hiddenEntries(paths, segments, range) : siblingEntries(paths, item));
|
|
150
|
+
return (_jsxs("li", { className: "docs-demo-crumb-item", "data-last": last || undefined, children: [index > 0 ? _jsx(ChevronRight, { className: "docs-demo-crumb-sep", width: 12, height: 12, "aria-hidden": "true" }) : null, _jsxs("button", { type: "button", className: "docs-demo-crumb", "data-crumb-last": last || undefined, "data-current": last || undefined, "aria-current": last ? "page" : undefined, "aria-haspopup": "menu", "aria-expanded": expanded, "aria-controls": expanded ? menuId : undefined, "aria-label": hidden ? "Show hidden path segments" : undefined, title: hidden ? undefined : item.kind === "src" ? undefined : item.path, onClick: (event) => open(id, event.currentTarget, label, entries()), onKeyDown: (event) => {
|
|
151
|
+
if (event.key !== "ArrowDown" && event.key !== "ArrowUp")
|
|
152
|
+
return;
|
|
153
|
+
event.preventDefault();
|
|
154
|
+
if (!expanded)
|
|
155
|
+
setMenu({ id, anchor: event.currentTarget, label, entries: entries() });
|
|
156
|
+
}, children: [last ? _jsx(FileCode2, { width: 13, height: 13, "aria-hidden": "true" }) : null, _jsx("span", { className: "docs-demo-crumb-text", children: hidden ? "…" : item.name })] })] }, id));
|
|
157
|
+
}) }), _jsx("ol", { ref: measure, className: "docs-demo-crumbs-list docs-demo-crumbs-measure", "aria-hidden": "true", children: [...segments.map((s) => s.name), "…"].map((name, index) => (_jsxs("li", { className: "docs-demo-crumb-item", children: [index > 0 ? _jsx(ChevronRight, { className: "docs-demo-crumb-sep", width: 12, height: 12 }) : null, _jsxs("span", { className: "docs-demo-crumb", children: [index === segments.length - 1 ? _jsx(FileCode2, { width: 13, height: 13 }) : null, _jsx("span", { className: "docs-demo-crumb-text", children: name })] })] }, `${index}:${name}`))) }), menu ? _jsx(CrumbMenu, { id: menuId, menu: menu, active: path, onPick: choose, onClose: close }, menu.id) : null] }));
|
|
158
|
+
}
|
|
159
|
+
function CrumbMenu({ id, menu, active, onPick, onClose }) {
|
|
160
|
+
const ref = useRef(null);
|
|
161
|
+
const { anchor } = menu;
|
|
162
|
+
const [at, setAt] = useState(() => {
|
|
163
|
+
const rect = anchor.getBoundingClientRect();
|
|
164
|
+
return { x: rect.left, y: rect.bottom + 4 };
|
|
165
|
+
});
|
|
166
|
+
// Place under the trigger, kept inside the viewport (flipped above when there is no room below),
|
|
167
|
+
// and focus the current entry.
|
|
168
|
+
useIsoLayoutEffect(() => {
|
|
169
|
+
const el = ref.current;
|
|
170
|
+
if (!el)
|
|
171
|
+
return;
|
|
172
|
+
const rect = anchor.getBoundingClientRect();
|
|
173
|
+
const { width, height } = el.getBoundingClientRect();
|
|
174
|
+
const x = Math.max(8, Math.min(rect.left, window.innerWidth - width - 8));
|
|
175
|
+
const below = rect.bottom + 4;
|
|
176
|
+
const y = below + height > window.innerHeight - 8 && rect.top - 4 - height >= 8 ? rect.top - 4 - height : below;
|
|
177
|
+
setAt({ x, y });
|
|
178
|
+
const items = Array.from(el.querySelectorAll("[role=menuitem]"));
|
|
179
|
+
(items.find((item) => item.hasAttribute("data-current")) ?? items[0])?.focus({ preventScroll: true });
|
|
180
|
+
}, [anchor]);
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
const onDown = (event) => {
|
|
183
|
+
const target = event.target;
|
|
184
|
+
if (!ref.current?.contains(target) && !anchor.contains(target))
|
|
185
|
+
onClose(false);
|
|
186
|
+
};
|
|
187
|
+
const onScroll = (event) => {
|
|
188
|
+
if (!ref.current?.contains(event.target))
|
|
189
|
+
onClose(false);
|
|
190
|
+
};
|
|
191
|
+
const dismiss = () => onClose(false);
|
|
192
|
+
document.addEventListener("pointerdown", onDown, true);
|
|
193
|
+
window.addEventListener("scroll", onScroll, true);
|
|
194
|
+
window.addEventListener("resize", dismiss);
|
|
195
|
+
return () => {
|
|
196
|
+
document.removeEventListener("pointerdown", onDown, true);
|
|
197
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
198
|
+
window.removeEventListener("resize", dismiss);
|
|
199
|
+
};
|
|
200
|
+
}, [anchor, onClose]);
|
|
201
|
+
const onKeyDown = (event) => {
|
|
202
|
+
const items = Array.from(event.currentTarget.querySelectorAll("[role=menuitem]"));
|
|
203
|
+
const index = items.indexOf(document.activeElement);
|
|
204
|
+
let next = -1;
|
|
205
|
+
if (event.key === "ArrowDown")
|
|
206
|
+
next = (index + 1) % items.length;
|
|
207
|
+
else if (event.key === "ArrowUp")
|
|
208
|
+
next = (index - 1 + items.length) % items.length;
|
|
209
|
+
else if (event.key === "Home")
|
|
210
|
+
next = 0;
|
|
211
|
+
else if (event.key === "End")
|
|
212
|
+
next = items.length - 1;
|
|
213
|
+
else if (event.key === "Escape") {
|
|
214
|
+
event.preventDefault();
|
|
215
|
+
event.stopPropagation();
|
|
216
|
+
onClose(true);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
else if (event.key === "Tab") {
|
|
220
|
+
onClose(true); // focus returns to the trigger; the Tab then moves on from there
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
else if (event.key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey) {
|
|
224
|
+
const key = event.key.toLowerCase();
|
|
225
|
+
const from = Math.max(index, 0);
|
|
226
|
+
for (let step = 1; step <= items.length; step++) {
|
|
227
|
+
const candidate = (from + step) % items.length;
|
|
228
|
+
if (items[candidate].textContent?.trim().toLowerCase().startsWith(key)) {
|
|
229
|
+
next = candidate;
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (next < 0)
|
|
235
|
+
return;
|
|
236
|
+
event.preventDefault();
|
|
237
|
+
items[next]?.focus();
|
|
238
|
+
};
|
|
239
|
+
const host = anchor.closest("[data-docstream], .docs-article") ?? document.body;
|
|
240
|
+
return createPortal(_jsx("div", { ref: ref, id: id, role: "menu", "aria-label": menu.label, className: "docs-demo-crumb-menu", style: { "--crumb-x": `${Math.round(at.x)}px`, "--crumb-y": `${Math.round(at.y)}px` }, onKeyDown: onKeyDown, children: menu.entries.map((entry) => {
|
|
241
|
+
const current = entryContains(entry, active);
|
|
242
|
+
return (_jsxs("button", { type: "button", role: "menuitem", tabIndex: -1, className: "docs-demo-crumb-option", "data-current": current || undefined, "aria-current": current || undefined, onClick: () => onPick(entry), children: [entry.kind === "dir" ? _jsx(Folder, { width: 13, height: 13, "aria-hidden": "true" }) : _jsx(FileCode2, { width: 13, height: 13, "aria-hidden": "true" }), _jsx("span", { className: "docs-demo-crumb-option-text", children: entry.name }), current ? _jsx(Check, { className: "docs-demo-crumb-check", width: 13, height: 13, "aria-hidden": "true" }) : null] }, `${entry.kind}:${entry.path}:${entry.name}`));
|
|
243
|
+
}) }), host);
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=PathBreadcrumb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PathBreadcrumb.js","sourceRoot":"","sources":["../../src/demo/PathBreadcrumb.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,8 +12,8 @@ export { DemoBlock, DemoFullscreen, DemoViewer, DocstreamDemoContext, DocstreamD
|
|
|
12
12
|
export type { DemoComponent, DemoComponentProps, DemoFile, DemoFullscreenProps, DemoMarkdownFormat, DemoMeta, DemoResolver, DemoRuntimeOptions, DemoVariant, DemoViewerProps, DemoVFS, DemoVFSChange, DemoVFSEntry, DocstreamClipboard, DocstreamClipboardPayload, GlobDemoResolverOptions, InlineDemoRuntime, InlineDemoRuntimeProps, ResolveDemosOptions, ResolvedDemo, } from "./demo/index.js";
|
|
13
13
|
export { CitationSources, DocstreamRefContext, InlineReference } from "./docs/reference.js";
|
|
14
14
|
export type { CitationPreview, CitationPreviewResolver, DocstreamRefContextValue } from "./docs/reference.js";
|
|
15
|
-
export { ReplayEmbed, ReplayPreview } from "./replay/ReplayPreview.js";
|
|
16
|
-
export type { ReplayEventsSource, ReplayEventsUrlSource, ReplayPageSource, ReplayPreviewProps, ReplaySource, } from "./replay/ReplayPreview.js";
|
|
15
|
+
export { ReplayEmbed, ReplayPreview, formatReplayTime } from "./replay/ReplayPreview.js";
|
|
16
|
+
export type { ReplayEventsSource, ReplayEventsUrlSource, ReplayPageSource, ReplayPreviewHandle, ReplayPreviewProps, ReplaySource, } from "./replay/ReplayPreview.js";
|
|
17
17
|
export type { ReplayEvent } from "./replay/types.js";
|
|
18
18
|
export { isReplayQaUrl, normalizeReplayEmbedUrl } from "./replay/url.js";
|
|
19
19
|
export { VideoEmbed } from "./video/index.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,YAAY,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACnE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAC3H,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC3E,OAAO,EACL,SAAS,EACT,cAAc,EACd,UAAU,EACV,oBAAoB,EACpB,2BAA2B,EAC3B,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,aAAa,EACb,wBAAwB,EACxB,oCAAoC,EACpC,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,EACnB,sBAAsB,EACtB,2BAA2B,EAC3B,UAAU,EACV,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,sBAAsB,EACtB,aAAa,EACb,aAAa,EACb,eAAe,EACf,cAAc,GACf,MAAM,QAAQ,CAAA;AACf,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,QAAQ,EACR,mBAAmB,EACnB,kBAAkB,EAClB,QAAQ,EACR,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,OAAO,EACP,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,yBAAyB,EACzB,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,YAAY,GACb,MAAM,QAAQ,CAAA;AACf,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACxF,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAA;AAC1G,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ export { CopyButton, copyText } from "./docs/copy.js";
|
|
|
8
8
|
export { getSyncedTab, setSyncedTab, useSyncedTab } from "./docs/tabs-sync.js";
|
|
9
9
|
export { DemoBlock, DemoFullscreen, DemoViewer, DocstreamDemoContext, DocstreamDemoRuntimeContext, DocstreamDemoVFSContext, DOCSTREAM_CLIPBOARD_TYPE, CopyDemoButton, createDemoVFS, createDocstreamClipboard, createDocstreamClipboardFromMarkdown, createGlobDemoResolver, docstreamClipboardItems, parseDocstreamClipboard, peekDemoSource, prefetchDemoSources, readDocstreamClipboard, readDocstreamClipboardItems, useDemoVFS, useDemoVFSFiles, defaultDemoMarkdown, inlineDemoMarkdown, inlineDemoNode, demoComponentFrom, demoFromSearch, demoHref, preloadDemos, resolveDemosToMarkdown, sortDemoFiles, toInlineFiles, useDemoResolver, useDemoRuntime, } from "./demo/index.js";
|
|
10
10
|
export { CitationSources, DocstreamRefContext, InlineReference } from "./docs/reference.js";
|
|
11
|
-
export { ReplayEmbed, ReplayPreview } from "./replay/ReplayPreview.js";
|
|
11
|
+
export { ReplayEmbed, ReplayPreview, formatReplayTime } from "./replay/ReplayPreview.js";
|
|
12
12
|
export { isReplayQaUrl, normalizeReplayEmbedUrl } from "./replay/url.js";
|
|
13
13
|
export { VideoEmbed } from "./video/index.js";
|
|
14
14
|
export { OpenApiOperation } from "./openapi/OpenApiOperation.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,sGAAsG;AACtG,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAEhD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEnD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC3E,OAAO,EACL,SAAS,EACT,cAAc,EACd,UAAU,EACV,oBAAoB,EACpB,2BAA2B,EAC3B,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,aAAa,EACb,wBAAwB,EACxB,oCAAoC,EACpC,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,EACnB,sBAAsB,EACtB,2BAA2B,EAC3B,UAAU,EACV,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,sBAAsB,EACtB,aAAa,EACb,aAAa,EACb,eAAe,EACf,cAAc,GACf,MAAM,QAAQ,CAAA;AAuBf,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAExF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,sGAAsG;AACtG,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAEhD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEnD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC3E,OAAO,EACL,SAAS,EACT,cAAc,EACd,UAAU,EACV,oBAAoB,EACpB,2BAA2B,EAC3B,uBAAuB,EACvB,wBAAwB,EACxB,cAAc,EACd,aAAa,EACb,wBAAwB,EACxB,oCAAoC,EACpC,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,EACnB,sBAAsB,EACtB,2BAA2B,EAC3B,UAAU,EACV,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,sBAAsB,EACtB,aAAa,EACb,aAAa,EACb,eAAe,EACf,cAAc,GACf,MAAM,QAAQ,CAAA;AAuBf,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAExF,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAUrF,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAC7D,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AASpF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACrD,OAAO,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAS3F,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACvG,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACrG,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,SAAS,EACT,cAAc,EACd,eAAe,EACf,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AACrF,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAA;AAC1E,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode, type Ref } from 'react';
|
|
2
|
+
import type { ReplayMarker, ReplayMarkerKind } from './markers.js';
|
|
3
|
+
import type { ReplayEvent } from './types.js';
|
|
4
|
+
export declare const DEFAULT_PLAYER_SPEEDS: number[];
|
|
5
|
+
export type ReplayChrome = 'browser' | 'minimal' | 'none';
|
|
6
|
+
export interface ReplayPreviewHandle {
|
|
7
|
+
play(offset?: number): void;
|
|
8
|
+
pause(offset?: number): void;
|
|
9
|
+
/** Jump to `ms` from the start, keeping the play state. */
|
|
10
|
+
seek(ms: number): void;
|
|
11
|
+
toggle(): void;
|
|
12
|
+
getCurrentTime(): number;
|
|
13
|
+
}
|
|
14
|
+
/** Options shared by every `ReplayPreview` (an `events` player or a `source` embed). */
|
|
15
|
+
export interface ReplayPlayerOptions {
|
|
16
|
+
/** Address shown in the URL bar (default: the recording's own URL, following its navigations). */
|
|
17
|
+
url?: string;
|
|
18
|
+
/** Accessible name of the player (default "Session replay"). */
|
|
19
|
+
title?: string;
|
|
20
|
+
/** `browser` (default): window buttons, URL bar, controls. `minimal`: the page and its controls in a rounded
|
|
21
|
+
* card. `none`: no frame, fill the parent (a hero, a lightbox). */
|
|
22
|
+
chrome?: ReplayChrome;
|
|
23
|
+
/** macOS window buttons in the browser chrome (default true). */
|
|
24
|
+
trafficLights?: boolean;
|
|
25
|
+
/** Play/pause, scrubber, speed and fullscreen (default true). */
|
|
26
|
+
controls?: boolean;
|
|
27
|
+
autoplay?: boolean;
|
|
28
|
+
loop?: boolean;
|
|
29
|
+
/** Where the player starts, in ms from the start. */
|
|
30
|
+
initialTime?: number;
|
|
31
|
+
/** Speeds the speed button cycles through; the first is the initial speed (default 1, 2, 4, 0.5). */
|
|
32
|
+
speeds?: number[];
|
|
33
|
+
/** Markers on the scrubber rail: `true` (default) reads them from the recording, `false` hides the rail,
|
|
34
|
+
* or pass your own. */
|
|
35
|
+
markers?: boolean | ReplayMarker[];
|
|
36
|
+
/** Which recorded marker kinds to show (default all). */
|
|
37
|
+
markerKinds?: ReplayMarkerKind[];
|
|
38
|
+
/** Draw the recorded pointer and click ripples (default true). */
|
|
39
|
+
cursor?: boolean;
|
|
40
|
+
/** `width` (default): the page scales to the frame's width and sets its height. `contain`: fill the frame's
|
|
41
|
+
* height (give it one) and letterbox the page. */
|
|
42
|
+
fit?: 'width' | 'contain';
|
|
43
|
+
/** Shown until the player is ready (default: a page-shaped skeleton). */
|
|
44
|
+
poster?: ReactNode;
|
|
45
|
+
/** Mount the player only once the frame nears the viewport (default true). */
|
|
46
|
+
lazy?: boolean;
|
|
47
|
+
/** Imperative control (play, pause, seek). */
|
|
48
|
+
playerRef?: Ref<ReplayPreviewHandle | null>;
|
|
49
|
+
onTimeUpdate?: (ms: number) => void;
|
|
50
|
+
onPlayingChange?: (playing: boolean) => void;
|
|
51
|
+
onMarkerPress?: (marker: ReplayMarker) => void;
|
|
52
|
+
className?: string;
|
|
53
|
+
style?: CSSProperties;
|
|
54
|
+
}
|
|
55
|
+
export declare function formatReplayTime(ms: number): string;
|
|
56
|
+
export declare function ReplayPlayer({ events, url, title, chrome, trafficLights, controls, autoplay, loop, initialTime, speeds, markers, markerKinds, cursor, fit, poster, lazy, playerRef, onTimeUpdate, onPlayingChange, onMarkerPress, className, style, }: ReplayPlayerOptions & {
|
|
57
|
+
events: ReplayEvent[];
|
|
58
|
+
}): import("react").JSX.Element;
|
|
59
|
+
export declare function BrowserBar({ href, trafficLights, loading }: {
|
|
60
|
+
href: string;
|
|
61
|
+
trafficLights: boolean;
|
|
62
|
+
loading: boolean;
|
|
63
|
+
}): import("react").JSX.Element;
|
|
64
|
+
//# sourceMappingURL=ReplayPlayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReplayPlayer.d.ts","sourceRoot":"","sources":["../../src/replay/ReplayPlayer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* The player behind `ReplayPreview`: an rrweb session replay in a browser frame. The headless `ReplayStage`
|
|
3
|
+
plays the recording (rrweb loads lazily, on first render); this adds the chrome around it: a URL bar that
|
|
4
|
+
follows the recording's navigations, play/pause, a scrubber with the recording's moments on a rail above
|
|
5
|
+
it, a speed control, fullscreen, and a drawn cursor whose clicks ripple. Until the player is ready a
|
|
6
|
+
poster (a page-shaped skeleton by default) holds the frame's exact size, so nothing jumps.
|
|
7
|
+
|
|
8
|
+
Plain React + plain CSS (`@layer docstream.replay` in styles.css, `[data-slot]` hooks). Keys (focus inside
|
|
9
|
+
the player): Space / K play and pause, ← → seek 5 s, J / L 10 s, < > speed, F fullscreen, Home / End jump
|
|
10
|
+
to the ends. Everything drawn over the page is a function of the playhead, so scrubbing backwards replays
|
|
11
|
+
the ripples and the URL too. */
|
|
12
|
+
import { memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
13
|
+
import { useReducedMotion } from 'framer-motion';
|
|
14
|
+
import { Lock, Maximize, Minimize, Pause, Play, RotateCcw } from 'lucide-react';
|
|
15
|
+
import { ReplayStage } from './ReplayStage.js';
|
|
16
|
+
import { getReplayMarkers, getReplayMeta, getReplayPointerTrack, replayPointerAt } from './markers.js';
|
|
17
|
+
const TRAFFIC_LIGHTS = ['#FF5F57', '#FEBC2E', '#28C840'];
|
|
18
|
+
export const DEFAULT_PLAYER_SPEEDS = [1, 2, 4, 0.5];
|
|
19
|
+
const RIPPLE_MS = 650;
|
|
20
|
+
export function formatReplayTime(ms) {
|
|
21
|
+
const s = Math.max(0, Math.floor(ms / 1000));
|
|
22
|
+
return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`;
|
|
23
|
+
}
|
|
24
|
+
const MARKER_NOUN = {
|
|
25
|
+
click: 'Click', error: 'Error', network: 'Request failed', navigation: 'Navigation', custom: 'Checkpoint',
|
|
26
|
+
};
|
|
27
|
+
export function ReplayPlayer({ events, url, title = 'Session replay', chrome = 'browser', trafficLights = true, controls = true, autoplay = false, loop = false, initialTime = 0, speeds = DEFAULT_PLAYER_SPEEDS, markers = true, markerKinds, cursor = true, fit = 'width', poster, lazy = true, playerRef, onTimeUpdate, onPlayingChange, onMarkerPress, className, style, }) {
|
|
28
|
+
const rootRef = useRef(null);
|
|
29
|
+
const stage = useRef(null);
|
|
30
|
+
const reduced = useReducedMotion();
|
|
31
|
+
const meta = useMemo(() => getReplayMeta(events), [events]);
|
|
32
|
+
const track = useMemo(() => getReplayPointerTrack(events), [events]);
|
|
33
|
+
const recorded = useMemo(() => getReplayMarkers(events), [events]);
|
|
34
|
+
const shown = useMemo(() => {
|
|
35
|
+
const list = Array.isArray(markers) ? markers : markers ? recorded : [];
|
|
36
|
+
return markerKinds ? list.filter((m) => markerKinds.includes(m.kind)) : list;
|
|
37
|
+
}, [markers, recorded, markerKinds]);
|
|
38
|
+
const [visible, setVisible] = useState(!lazy);
|
|
39
|
+
const [ready, setReady] = useState(false);
|
|
40
|
+
const [posterGone, setPosterGone] = useState(false);
|
|
41
|
+
const [time, setTime] = useState(initialTime);
|
|
42
|
+
const [playing, setPlaying] = useState(false);
|
|
43
|
+
const [finished, setFinished] = useState(false);
|
|
44
|
+
const [speedIndex, setSpeedIndex] = useState(0);
|
|
45
|
+
const [fullscreen, setFullscreen] = useState(false);
|
|
46
|
+
const duration = meta.duration;
|
|
47
|
+
const speedList = speeds.length ? speeds : DEFAULT_PLAYER_SPEEDS;
|
|
48
|
+
const speed = speedList[speedIndex % speedList.length] ?? 1;
|
|
49
|
+
// Lazy mount: start the player (and fetch rrweb) once the frame is near the viewport.
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (visible)
|
|
52
|
+
return;
|
|
53
|
+
const el = rootRef.current;
|
|
54
|
+
if (!el || typeof IntersectionObserver === 'undefined')
|
|
55
|
+
return setVisible(true);
|
|
56
|
+
const io = new IntersectionObserver((entries) => entries.some((e) => e.isIntersecting) && setVisible(true), { rootMargin: '240px' });
|
|
57
|
+
io.observe(el);
|
|
58
|
+
return () => io.disconnect();
|
|
59
|
+
}, [visible]);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const on = () => setFullscreen(document.fullscreenElement === rootRef.current);
|
|
62
|
+
document.addEventListener('fullscreenchange', on);
|
|
63
|
+
return () => document.removeEventListener('fullscreenchange', on);
|
|
64
|
+
}, []);
|
|
65
|
+
// The poster dissolves (CSS) once the page is ready, then leaves the tree.
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!ready)
|
|
68
|
+
return setPosterGone(false);
|
|
69
|
+
const timer = setTimeout(() => setPosterGone(true), reduced ? 120 : 260);
|
|
70
|
+
return () => clearTimeout(timer);
|
|
71
|
+
}, [ready, reduced]);
|
|
72
|
+
const handle = useMemo(() => ({
|
|
73
|
+
play: (offset) => { setFinished(false); stage.current?.play(offset); },
|
|
74
|
+
pause: (offset) => stage.current?.pause(offset),
|
|
75
|
+
seek: (ms) => { setFinished(false); stage.current?.seek(ms); },
|
|
76
|
+
toggle: () => { setFinished(false); stage.current?.toggle(); },
|
|
77
|
+
getCurrentTime: () => stage.current?.getCurrentTime() ?? 0,
|
|
78
|
+
}), []);
|
|
79
|
+
useImperativeHandle(playerRef, () => handle, [handle]);
|
|
80
|
+
const onTime = useCallback((ms) => { setTime(ms); onTimeUpdate?.(ms); }, [onTimeUpdate]);
|
|
81
|
+
const onPlaying = useCallback((p) => {
|
|
82
|
+
setPlaying(p);
|
|
83
|
+
if (p)
|
|
84
|
+
setFinished(false);
|
|
85
|
+
onPlayingChange?.(p);
|
|
86
|
+
}, [onPlayingChange]);
|
|
87
|
+
const toggle = () => handle.toggle();
|
|
88
|
+
const seekBy = (d) => handle.seek(Math.max(0, Math.min(duration, time + d)));
|
|
89
|
+
const cycleSpeed = (d = 1) => setSpeedIndex((i) => (i + d + speedList.length) % speedList.length);
|
|
90
|
+
const toggleFullscreen = () => {
|
|
91
|
+
const el = rootRef.current;
|
|
92
|
+
if (!el)
|
|
93
|
+
return;
|
|
94
|
+
if (document.fullscreenElement)
|
|
95
|
+
void document.exitFullscreen?.().catch(() => { });
|
|
96
|
+
else
|
|
97
|
+
void el.requestFullscreen?.().catch(() => { });
|
|
98
|
+
};
|
|
99
|
+
const jumpTo = (m) => {
|
|
100
|
+
handle.seek(Math.max(0, m.time - 250));
|
|
101
|
+
onMarkerPress?.(m);
|
|
102
|
+
};
|
|
103
|
+
const onKeyDown = (e) => {
|
|
104
|
+
const target = e.target;
|
|
105
|
+
if (e.metaKey || e.ctrlKey || e.altKey || target.closest('input:not([type=range]), textarea, [contenteditable=true]'))
|
|
106
|
+
return;
|
|
107
|
+
const onSlider = target.matches('input[type=range]');
|
|
108
|
+
const run = (fn) => { e.preventDefault(); fn(); };
|
|
109
|
+
switch (e.key) {
|
|
110
|
+
case ' ':
|
|
111
|
+
case 'k':
|
|
112
|
+
case 'K':
|
|
113
|
+
if (target.closest('button') && e.key === ' ')
|
|
114
|
+
return;
|
|
115
|
+
return run(toggle);
|
|
116
|
+
case 'ArrowLeft':
|
|
117
|
+
if (onSlider)
|
|
118
|
+
return;
|
|
119
|
+
return run(() => seekBy(-5000));
|
|
120
|
+
case 'ArrowRight':
|
|
121
|
+
if (onSlider)
|
|
122
|
+
return;
|
|
123
|
+
return run(() => seekBy(5000));
|
|
124
|
+
case 'j':
|
|
125
|
+
case 'J': return run(() => seekBy(-10000));
|
|
126
|
+
case 'l':
|
|
127
|
+
case 'L': return run(() => seekBy(10000));
|
|
128
|
+
case 'Home':
|
|
129
|
+
if (onSlider)
|
|
130
|
+
return;
|
|
131
|
+
return run(() => handle.seek(0));
|
|
132
|
+
case 'End':
|
|
133
|
+
if (onSlider)
|
|
134
|
+
return;
|
|
135
|
+
return run(() => handle.seek(duration));
|
|
136
|
+
case '>':
|
|
137
|
+
case '.': return run(() => cycleSpeed(1));
|
|
138
|
+
case '<':
|
|
139
|
+
case ',': return run(() => cycleSpeed(-1));
|
|
140
|
+
case 'f':
|
|
141
|
+
case 'F': return run(toggleFullscreen);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
// The URL bar follows the recording: the latest navigation at or before the playhead.
|
|
145
|
+
const href = useMemo(() => {
|
|
146
|
+
if (url)
|
|
147
|
+
return url;
|
|
148
|
+
let current = meta.href ?? '';
|
|
149
|
+
for (const m of recorded)
|
|
150
|
+
if (m.kind === 'navigation' && m.time <= time && m.detail)
|
|
151
|
+
current = m.detail;
|
|
152
|
+
return current;
|
|
153
|
+
}, [url, meta.href, recorded, time]);
|
|
154
|
+
const w = meta.width || 1280;
|
|
155
|
+
const h = meta.height || 800;
|
|
156
|
+
const contain = fit === 'contain' || fullscreen;
|
|
157
|
+
return (_jsxs("div", { ref: rootRef, className: className ? `replay-preview ${className}` : 'replay-preview', "data-slot": "replay-preview", "data-replay-preview": "rrweb", "data-chrome": chrome, "data-fit": contain ? 'contain' : 'width', "data-state": !ready ? 'loading' : playing ? 'playing' : 'paused', "data-fullscreen": fullscreen || undefined, role: "group", "aria-label": title, "aria-busy": !ready || undefined, tabIndex: -1, onKeyDown: onKeyDown,
|
|
158
|
+
// A click anywhere in the player (the page, the frame) makes its keys live, without a focus ring.
|
|
159
|
+
onPointerDown: () => {
|
|
160
|
+
const root = rootRef.current;
|
|
161
|
+
if (root && !root.contains(document.activeElement))
|
|
162
|
+
root.focus({ preventScroll: true });
|
|
163
|
+
}, style: style, children: [chrome === 'browser' ? _jsx(BrowserBar, { href: href, trafficLights: trafficLights, loading: !ready && meta.playable }) : null, _jsxs("div", { "data-slot": "replay-preview-stage", style: { '--replay-ar': `${w} / ${h}` }, children: [visible && meta.playable ? (_jsx(ReplayStage, { ref: stage, events: events, fit: "contain", speed: speed, loop: loop, autoplay: autoplay, initialTime: initialTime, showMouse: false, className: "replay-preview__page", overlay: cursor ? _jsx(PointerOverlay, { track: track, time: time, reduced: !!reduced }) : null, onReady: () => setReady(true), onTimeUpdate: onTime, onPlayingChange: onPlaying, onFinish: () => setFinished(true) })) : null, !posterGone ? (_jsx("div", { "data-slot": "replay-preview-poster", "data-leaving": ready || undefined, children: poster ?? _jsx(PagePoster, { empty: !meta.playable }) })) : null, controls ? (_jsx("div", { "data-slot": "replay-preview-center", "data-show": (ready && !playing) || undefined, children: _jsx("button", { type: "button", tabIndex: -1, "aria-label": finished ? 'Replay from the start' : 'Play',
|
|
164
|
+
// The overlay leaves as playback starts; keep focus (and the keys) in the player.
|
|
165
|
+
onClick: () => { rootRef.current?.focus({ preventScroll: true }); toggle(); }, children: finished ? _jsx(RotateCcw, { size: 22, strokeWidth: 2.4 }) : _jsx(Play, { size: 22, strokeWidth: 2.4, fill: "currentColor" }) }) })) : null] }), controls ? (_jsxs("div", { "data-slot": "replay-preview-controls", children: [_jsx("button", { type: "button", "data-slot": "replay-preview-play", "aria-label": playing ? 'Pause' : 'Play', onClick: toggle, disabled: !ready, children: playing ? _jsx(Pause, { size: 15, fill: "currentColor" }) : _jsx(Play, { size: 15, fill: "currentColor" }) }), _jsxs("span", { "data-slot": "replay-preview-time", children: [_jsx("span", { children: formatReplayTime(time) }), " / ", formatReplayTime(duration)] }), _jsx(Scrubber, { time: time, duration: duration, markers: shown, disabled: !ready, onSeek: (ms) => handle.seek(ms), onMarker: jumpTo }), _jsx("button", { type: "button", "data-slot": "replay-preview-speed", "aria-label": `Playback speed ${speed}×`, onClick: () => cycleSpeed(1), children: `${speed}×` }), _jsx("button", { type: "button", "data-slot": "replay-preview-fullscreen", "aria-label": fullscreen ? 'Exit full screen' : 'Full screen', onClick: toggleFullscreen, children: fullscreen ? _jsx(Minimize, { size: 15 }) : _jsx(Maximize, { size: 15 }) })] })) : null] }));
|
|
166
|
+
}
|
|
167
|
+
/* ── parts ───────────────────────────────────────────────────────────────────────── */
|
|
168
|
+
export function BrowserBar({ href, trafficLights, loading }) {
|
|
169
|
+
const shown = href.replace(/^https?:\/\//, '');
|
|
170
|
+
const slash = shown.indexOf('/');
|
|
171
|
+
const host = slash < 0 ? shown : shown.slice(0, slash);
|
|
172
|
+
const path = slash < 0 ? '' : shown.slice(slash);
|
|
173
|
+
return (_jsxs("div", { "data-slot": "replay-preview-bar", children: [trafficLights ? (_jsx("span", { "aria-hidden": "true", "data-slot": "replay-preview-lights", children: TRAFFIC_LIGHTS.map((c) => _jsx("span", { style: { '--light': c } }, c)) })) : null, _jsxs("div", { "data-slot": "replay-preview-address", children: [_jsx("span", { "data-slot": "replay-preview-lock", "aria-hidden": "true", children: loading ? _jsx("span", { "data-slot": "replay-preview-spinner" }) : _jsx(Lock, { size: 10, strokeWidth: 2.5 }) }), _jsxs("span", { "data-slot": "replay-preview-url", title: href, children: [_jsx("span", { children: host }), _jsx("span", { children: path })] })] }), trafficLights ? _jsx("span", { "aria-hidden": "true", "data-slot": "replay-preview-lights-spacer" }) : null] }));
|
|
174
|
+
}
|
|
175
|
+
/** The recorded pointer and its click ripples, in recorded-viewport coordinates. */
|
|
176
|
+
const PointerOverlay = memo(function PointerOverlay({ track, time, reduced }) {
|
|
177
|
+
const at = replayPointerAt(track, time);
|
|
178
|
+
const ripples = track.clicks.filter((c) => time >= c.time && time - c.time < RIPPLE_MS);
|
|
179
|
+
return (_jsxs("div", { "data-slot": "replay-preview-pointer", "aria-hidden": "true", children: [ripples.map((c) => {
|
|
180
|
+
const p = (time - c.time) / RIPPLE_MS;
|
|
181
|
+
const ease = 1 - (1 - p) ** 3;
|
|
182
|
+
return (_jsx("span", { "data-slot": "replay-preview-ripple", style: {
|
|
183
|
+
left: c.x, top: c.y,
|
|
184
|
+
transform: `translate(-50%, -50%) scale(calc(${reduced ? 0.8 : 0.25 + ease * 1.05} / var(--replay-scale, 1)))`,
|
|
185
|
+
opacity: 1 - p,
|
|
186
|
+
} }, c.time));
|
|
187
|
+
}), at ? (_jsx("svg", { "data-slot": "replay-preview-cursor", viewBox: "0 0 20 24", width: "20", height: "24", style: { left: at.x - 2, top: at.y - 1, transform: 'scale(calc(1 / var(--replay-scale, 1)))' }, children: _jsx("path", { d: "M2.5 1.5v18.2l4.7-4.5 3 6.8 3.1-1.4-3-6.6h6.5z", fill: "#000", stroke: "#fff", strokeWidth: "1.4", strokeLinejoin: "round" }) })) : null] }));
|
|
188
|
+
});
|
|
189
|
+
function Scrubber({ time, duration, markers, disabled, onSeek, onMarker }) {
|
|
190
|
+
const max = Math.max(1, duration);
|
|
191
|
+
const value = Math.min(time, max);
|
|
192
|
+
// Dots closer than ~1.5% of the timeline would sit on top of each other: step each one 10px right of the last.
|
|
193
|
+
const nudges = useMemo(() => {
|
|
194
|
+
const out = new Map();
|
|
195
|
+
let last = null;
|
|
196
|
+
for (const m of [...markers].sort((a, b) => a.time - b.time)) {
|
|
197
|
+
if (m.kind === 'click')
|
|
198
|
+
continue;
|
|
199
|
+
const n = last && m.time - last.time < max * 0.015 ? last.n + 1 : 0;
|
|
200
|
+
out.set(m.id, n);
|
|
201
|
+
last = { time: m.time, n };
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
}, [markers, max]);
|
|
205
|
+
return (_jsxs("div", { "data-slot": "replay-preview-scrubber", "data-markers": markers.length ? '' : undefined, children: [markers.length ? (_jsx("div", { "data-slot": "replay-preview-markers", children: markers.map((m) => (_jsx("button", { type: "button", "data-kind": m.kind, "aria-label": `${m.label} at ${formatReplayTime(m.time)}`, title: `${formatReplayTime(m.time)} · ${MARKER_NOUN[m.kind]}\n${m.detail ? `${m.label} — ${m.detail}` : m.label}`, style: { left: `calc(${(m.time / max) * 100}% + ${(nudges.get(m.id) ?? 0) * 10}px)` }, onClick: () => onMarker(m), disabled: disabled }, m.id))) })) : null, _jsx("input", { type: "range", "aria-label": "Playback position", "aria-valuetext": `${formatReplayTime(value)} of ${formatReplayTime(duration)}`, min: 0, max: max, step: 10, value: value, disabled: disabled, style: { '--replay-progress': `${(value / max) * 100}%` }, onChange: (e) => onSeek(Number(e.currentTarget.value)) })] }));
|
|
206
|
+
}
|
|
207
|
+
/** A page-shaped placeholder: header, hero lines and a card grid, sized to the frame. */
|
|
208
|
+
function PagePoster({ empty }) {
|
|
209
|
+
return (_jsxs("div", { "data-slot": "replay-preview-skeleton", children: [_jsxs("div", { "data-part": "header", children: [_jsx("i", { style: { width: '12%' } }), _jsx("i", { style: { width: '8%' } }), _jsx("i", { style: { width: '8%' } }), _jsx("span", {}), _jsx("i", { "data-tone": "block", style: { width: '9%' } })] }), _jsxs("div", { "data-part": "hero", children: [_jsx("i", { style: { width: '38%', height: 12 } }), _jsx("i", { style: { width: '24%', height: 8 } })] }), _jsx("div", { "data-part": "grid", children: [0, 1, 2, 3].map((i) => _jsx("i", { "data-tone": "block" }, i)) }), empty ? _jsx("span", { "data-part": "empty", children: _jsx("span", { children: "This recording has no page snapshot to play" }) }) : null] }));
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=ReplayPlayer.js.map
|