@reactra/replay-devtools 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/chrome.d.ts +81 -0
- package/dist/chrome.d.ts.map +1 -0
- package/dist/chrome.js +156 -0
- package/dist/chrome.js.map +1 -0
- package/dist/compare.d.ts +31 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +165 -0
- package/dist/compare.js.map +1 -0
- package/dist/helpers.d.ts +101 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +236 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/panel.d.ts +25 -0
- package/dist/panel.d.ts.map +1 -0
- package/dist/panel.js +725 -0
- package/dist/panel.js.map +1 -0
- package/dist/styles.d.ts +10 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +388 -0
- package/dist/styles.js.map +1 -0
- package/dist/timeline.d.ts +34 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +144 -0
- package/dist/timeline.js.map +1 -0
- package/dist/tokens.d.ts +10 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +317 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Akhil Shastri and the Reactra contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# @reactra/replay-devtools
|
|
2
|
+
|
|
3
|
+
The Reactra **session-recorder panel** — a drop-in devtools drawer for apps
|
|
4
|
+
using `uses replayable` (Replay spec §4). Mount it anywhere in your tree:
|
|
5
|
+
|
|
6
|
+
```tsx
|
|
7
|
+
import { ReplayDevtools } from "@reactra/replay-devtools"
|
|
8
|
+
|
|
9
|
+
// at the end of your root view / layout:
|
|
10
|
+
<ReplayDevtools endpoint="/replay" token={import.meta.env.VITE_REPLAY_TOKEN} />
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
It is a **UI companion** package (Runtime spec §2): hand-mounted, never
|
|
14
|
+
imported by compiler-emitted code — an app that doesn't mount it ships none
|
|
15
|
+
of it.
|
|
16
|
+
|
|
17
|
+
## What you get
|
|
18
|
+
|
|
19
|
+
- **Finish & inspect** — finalize the live recording into a `SessionBundle`.
|
|
20
|
+
- **Visual timeline** — color-coded ticks per event (▶ action · ⬛ keyframe ·
|
|
21
|
+
Δ delta · ◆ resource · +/− mount · ⚠ gap); keyframes render taller; click
|
|
22
|
+
anywhere to scrub.
|
|
23
|
+
- **Transport** — ⏮/⏭ event stepping, ▶ auto-playback at recorded pacing
|
|
24
|
+
(1×/2×/4×, long idles clamped), keyboard: `←`/`→` step, `space` play/pause.
|
|
25
|
+
- **Per-instance state cards** — `ReplayPlayer.statesAt` folds (Replay §5
|
|
26
|
+
v1.8); keys the current stop changed are highlighted.
|
|
27
|
+
- **Filters** — toggle event types and instances on the timeline + table.
|
|
28
|
+
- **Live re-drive** — drives the folded state into the mounted components as
|
|
29
|
+
you scrub/play, so the live app replays in step with the timeline (Replay §5;
|
|
30
|
+
recording is suspended meanwhile). **On by default** when a session loads
|
|
31
|
+
(parity with the devtools Time Travel tab); the toggle switches to
|
|
32
|
+
**inspect-only** to read recorded state without touching the live app.
|
|
33
|
+
- **Server sessions** — list/load/delete sessions from a replay ingest
|
|
34
|
+
server, with meta chips and a `key=value` filter
|
|
35
|
+
(`GET /sessions?meta.<key>=<value>`).
|
|
36
|
+
|
|
37
|
+
## Props
|
|
38
|
+
|
|
39
|
+
| Prop | Default | Purpose |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| `endpoint` | `"/replay"` | Base path of the server's HTTP **read** API |
|
|
42
|
+
| `token` | — | `Authorization: Bearer <token>` sugar |
|
|
43
|
+
| `headers` | — | Custom auth-header supplier (wins over `token`) |
|
|
44
|
+
| `defaultOpen` | `false` | Start with the drawer open |
|
|
45
|
+
| `drive` | `true` | Show the live re-drive toggle |
|
|
46
|
+
|
|
47
|
+
## The server read API (not a Reactra spec contract)
|
|
48
|
+
|
|
49
|
+
The panel fetches these routes, matching `examples/replay-server`:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
GET {endpoint}/sessions[?meta.<key>=<value>&since=<ms>] → SessionSummary[]
|
|
53
|
+
GET {endpoint}/sessions/:id/bundle → SessionBundle
|
|
54
|
+
DELETE {endpoint}/sessions/:id → 204
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This shape is **owned by the server** (`examples/replay-server/PLAN.md`),
|
|
58
|
+
not by a Reactra spec — any backend serving the same shapes works. The
|
|
59
|
+
streaming **ingest transport** (`createReplayServerSink` — WebSocket NDJSON,
|
|
60
|
+
first-message auth, sendBeacon tail) is browser-only user code and stays in
|
|
61
|
+
the examples (see `examples/board-replay/src/replayTransport.ts`); wire it
|
|
62
|
+
into `configureReplay({ sink })` yourself.
|
|
63
|
+
|
|
64
|
+
## Node-portability
|
|
65
|
+
|
|
66
|
+
No JSX in source (written against `React.createElement`), no `.css` imports
|
|
67
|
+
(one `<style>` tag injected behind a `typeof document` guard), browser
|
|
68
|
+
globals only inside handlers. `import("@reactra/replay-devtools")` works
|
|
69
|
+
under plain Node ≥ 22.18.
|
package/dist/chrome.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { MouseEvent as ReactMouseEvent } from "react";
|
|
2
|
+
/** Theme mode — "system" defers to prefers-color-scheme CSS. */
|
|
3
|
+
export type PanelTheme = "system" | "light" | "dark";
|
|
4
|
+
/** Options for `usePanelHeight`. */
|
|
5
|
+
export interface PanelHeightOptions {
|
|
6
|
+
/** Initial height when no persisted value is found (default 400). */
|
|
7
|
+
defaultHeight?: number;
|
|
8
|
+
/** Minimum clamped height in px (default 100). */
|
|
9
|
+
min?: number;
|
|
10
|
+
/** Maximum clamped height in px (default 1200). */
|
|
11
|
+
max?: number;
|
|
12
|
+
}
|
|
13
|
+
/** Props for the drag-resize handle element. */
|
|
14
|
+
export interface ResizeHandleProps {
|
|
15
|
+
/** Attach to the resize-handle element's `onMouseDown`. */
|
|
16
|
+
onMouseDown: (e: ReactMouseEvent) => void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Manage panel height with drag-to-resize and localStorage persistence.
|
|
20
|
+
*
|
|
21
|
+
* @param storageKey - Package-namespaced key (e.g. `"@scope/pkg:panel-height"`)
|
|
22
|
+
* to avoid collisions with co-mounted panels that share the same origin.
|
|
23
|
+
* @param opts - Clamp bounds and default height.
|
|
24
|
+
* @returns `[height, handleProps]` — apply `handleProps` to the resize-handle element.
|
|
25
|
+
*/
|
|
26
|
+
export declare const usePanelHeight: (storageKey: string, opts?: PanelHeightOptions) => [number, ResizeHandleProps];
|
|
27
|
+
/**
|
|
28
|
+
* Manage the panel's System→Light→Dark theme with localStorage persistence.
|
|
29
|
+
*
|
|
30
|
+
* @param storageKey - Package-namespaced key (e.g. `"@scope/pkg:theme"`)
|
|
31
|
+
* to avoid collisions with co-mounted panels that share the same origin.
|
|
32
|
+
* @returns `[theme, cycleTheme]` — `cycleTheme` advances System→Light→Dark→System.
|
|
33
|
+
*/
|
|
34
|
+
export declare const usePanelTheme: (storageKey: string) => [PanelTheme, () => void];
|
|
35
|
+
/** Options for `openPanelWindow`. */
|
|
36
|
+
export interface OpenPanelWindowOptions {
|
|
37
|
+
/** Unique window name (used as the `target` argument to `window.open`). */
|
|
38
|
+
name: string;
|
|
39
|
+
/** Browser window title. */
|
|
40
|
+
title: string;
|
|
41
|
+
/** Initial width (px, default 1100). */
|
|
42
|
+
width?: number;
|
|
43
|
+
/** Initial height (px, default 520). */
|
|
44
|
+
height?: number;
|
|
45
|
+
/** Background color for the popup body (default `"#15171c"`). */
|
|
46
|
+
background?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Style injectors to call once the window is ready. Each receives the
|
|
49
|
+
* popup's `document` so it can append a `<style>` tag. Do NOT call
|
|
50
|
+
* `createPortal` here — that stays at the call site (C2).
|
|
51
|
+
*/
|
|
52
|
+
injectStyles?: Array<(doc: Document) => void>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Open a blank popup window and prepare it for `createPortal` re-attach.
|
|
56
|
+
*
|
|
57
|
+
* Returns the new `Window` on success, or `null` if the browser blocked it.
|
|
58
|
+
* `createPortal` stays at the caller's call site (C2 — no react-dom dep here).
|
|
59
|
+
*/
|
|
60
|
+
export declare const openPanelWindow: (opts: OpenPanelWindowOptions) => Window | null;
|
|
61
|
+
/** Modifier keys for `useToggleHotkey`. */
|
|
62
|
+
export interface HotkeyModifiers {
|
|
63
|
+
shift?: boolean;
|
|
64
|
+
alt?: boolean;
|
|
65
|
+
ctrl?: boolean;
|
|
66
|
+
/** Match `e.metaKey` (Cmd on Mac). */
|
|
67
|
+
meta?: boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Register a global keyboard shortcut that fires `onTrigger`.
|
|
71
|
+
*
|
|
72
|
+
* The listener attaches to `document` in a `useEffect` so it's browser-only
|
|
73
|
+
* and cleaned up on unmount.
|
|
74
|
+
*
|
|
75
|
+
* @param key - The key string (e.g. `"D"`) matched case-insensitively.
|
|
76
|
+
* @param modifiers - Which modifier keys must be held.
|
|
77
|
+
* @param onTrigger - `onTrigger` may be a fresh closure each render — its
|
|
78
|
+
* latest identity is always used (captured via ref).
|
|
79
|
+
*/
|
|
80
|
+
export declare const useToggleHotkey: (key: string, modifiers: HotkeyModifiers, onTrigger: () => void) => void;
|
|
81
|
+
//# sourceMappingURL=chrome.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../src/chrome.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,IAAI,eAAe,EAAE,MAAM,OAAO,CAAA;AAE1D,gEAAgE;AAChE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAA;AAEpD,oCAAoC;AACpC,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,kDAAkD;IAClD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,WAAW,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,IAAI,CAAA;CAC1C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GACzB,YAAY,MAAM,EAClB,OAAM,kBAAuB,KAC5B,CAAC,MAAM,EAAE,iBAAiB,CAkD5B,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,YAAY,MAAM,KAAG,CAAC,UAAU,EAAE,MAAM,IAAI,CAkBzE,CAAA;AAED,qCAAqC;AACrC,MAAM,WAAW,sBAAsB;IACrC,2EAA2E;IAC3E,IAAI,EAAE,MAAM,CAAA;IACZ,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAA;CAC9C;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,sBAAsB,KAAG,MAAM,GAAG,IAYvE,CAAA;AAED,2CAA2C;AAC3C,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,sCAAsC;IACtC,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAC1B,KAAK,MAAM,EACX,WAAW,eAAe,EAC1B,WAAW,MAAM,IAAI,KACpB,IAoBF,CAAA"}
|
package/dist/chrome.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// @reactra/replay-devtools/chrome — headless window-chrome helpers.
|
|
2
|
+
//
|
|
3
|
+
// Owner: reactra-runtime-spec.md §3 (/chrome subpath row, Discussion v11).
|
|
4
|
+
// Shared by `@reactra/replay-devtools` (panel.ts) and `@reactra/devtools`
|
|
5
|
+
// (panel.ts). C1: one implementation, not two forks.
|
|
6
|
+
//
|
|
7
|
+
// Node-portability rules (C5/C6; CLAUDE.md §6):
|
|
8
|
+
// - No browser global at module top level.
|
|
9
|
+
// - Every `window`/`document`/`localStorage` access is inside a useState
|
|
10
|
+
// initializer, a useEffect, or an event handler — React only runs these
|
|
11
|
+
// client-side.
|
|
12
|
+
import { useEffect, useRef, useState } from "react";
|
|
13
|
+
/**
|
|
14
|
+
* Manage panel height with drag-to-resize and localStorage persistence.
|
|
15
|
+
*
|
|
16
|
+
* @param storageKey - Package-namespaced key (e.g. `"@scope/pkg:panel-height"`)
|
|
17
|
+
* to avoid collisions with co-mounted panels that share the same origin.
|
|
18
|
+
* @param opts - Clamp bounds and default height.
|
|
19
|
+
* @returns `[height, handleProps]` — apply `handleProps` to the resize-handle element.
|
|
20
|
+
*/
|
|
21
|
+
export const usePanelHeight = (storageKey, opts = {}) => {
|
|
22
|
+
const { defaultHeight = 400, min = 100, max = 1200 } = opts;
|
|
23
|
+
const [height, setHeight] = useState(() => {
|
|
24
|
+
// localStorage access is inside the initializer — React only calls this
|
|
25
|
+
// once on the client side.
|
|
26
|
+
if (typeof localStorage !== "undefined") {
|
|
27
|
+
const saved = localStorage.getItem(storageKey);
|
|
28
|
+
if (saved) {
|
|
29
|
+
const n = parseInt(saved, 10);
|
|
30
|
+
if (!Number.isNaN(n) && n >= min && n <= max)
|
|
31
|
+
return n;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return defaultHeight;
|
|
35
|
+
});
|
|
36
|
+
// Drag state lives in a ref — the mousemove/mouseup listeners are imperative
|
|
37
|
+
// and fire outside React's event loop.
|
|
38
|
+
const dragRef = useRef(null);
|
|
39
|
+
const onMouseDown = (e) => {
|
|
40
|
+
e.preventDefault();
|
|
41
|
+
if (typeof window === "undefined")
|
|
42
|
+
return;
|
|
43
|
+
dragRef.current = { startY: e.clientY, startH: height };
|
|
44
|
+
const onMove = (ev) => {
|
|
45
|
+
if (!dragRef.current)
|
|
46
|
+
return;
|
|
47
|
+
// Dragging UP (negative deltaY) grows the panel (it anchors to the bottom).
|
|
48
|
+
const delta = dragRef.current.startY - ev.clientY;
|
|
49
|
+
const next = Math.max(min, Math.min(max, dragRef.current.startH + delta));
|
|
50
|
+
setHeight(next);
|
|
51
|
+
};
|
|
52
|
+
const onUp = (ev) => {
|
|
53
|
+
if (!dragRef.current)
|
|
54
|
+
return;
|
|
55
|
+
const delta = dragRef.current.startY - ev.clientY;
|
|
56
|
+
const next = Math.max(min, Math.min(max, dragRef.current.startH + delta));
|
|
57
|
+
dragRef.current = null;
|
|
58
|
+
setHeight(next);
|
|
59
|
+
try {
|
|
60
|
+
localStorage.setItem(storageKey, String(next));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Quota exceeded or sandboxed iframe — ignore.
|
|
64
|
+
}
|
|
65
|
+
document.removeEventListener("mousemove", onMove);
|
|
66
|
+
document.removeEventListener("mouseup", onUp);
|
|
67
|
+
};
|
|
68
|
+
document.addEventListener("mousemove", onMove);
|
|
69
|
+
document.addEventListener("mouseup", onUp);
|
|
70
|
+
};
|
|
71
|
+
return [height, { onMouseDown }];
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Manage the panel's System→Light→Dark theme with localStorage persistence.
|
|
75
|
+
*
|
|
76
|
+
* @param storageKey - Package-namespaced key (e.g. `"@scope/pkg:theme"`)
|
|
77
|
+
* to avoid collisions with co-mounted panels that share the same origin.
|
|
78
|
+
* @returns `[theme, cycleTheme]` — `cycleTheme` advances System→Light→Dark→System.
|
|
79
|
+
*/
|
|
80
|
+
export const usePanelTheme = (storageKey) => {
|
|
81
|
+
const [theme, setTheme] = useState(() => {
|
|
82
|
+
if (typeof localStorage !== "undefined") {
|
|
83
|
+
const saved = localStorage.getItem(storageKey);
|
|
84
|
+
if (saved === "light" || saved === "dark" || saved === "system")
|
|
85
|
+
return saved;
|
|
86
|
+
}
|
|
87
|
+
return "system";
|
|
88
|
+
});
|
|
89
|
+
const cycleTheme = () => {
|
|
90
|
+
const next = theme === "system" ? "light" : theme === "light" ? "dark" : "system";
|
|
91
|
+
setTheme(next);
|
|
92
|
+
if (typeof localStorage !== "undefined") {
|
|
93
|
+
localStorage.setItem(storageKey, next);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
return [theme, cycleTheme];
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Open a blank popup window and prepare it for `createPortal` re-attach.
|
|
100
|
+
*
|
|
101
|
+
* Returns the new `Window` on success, or `null` if the browser blocked it.
|
|
102
|
+
* `createPortal` stays at the caller's call site (C2 — no react-dom dep here).
|
|
103
|
+
*/
|
|
104
|
+
export const openPanelWindow = (opts) => {
|
|
105
|
+
// `window` access is intentional here: this function is only ever called
|
|
106
|
+
// from an event handler (a button click), never at module top level.
|
|
107
|
+
if (typeof window === "undefined")
|
|
108
|
+
return null;
|
|
109
|
+
const { name, title, width = 1100, height = 520, background = "#15171c", injectStyles = [] } = opts;
|
|
110
|
+
const w = window.open("", name, `width=${width},height=${height}`);
|
|
111
|
+
if (!w)
|
|
112
|
+
return null;
|
|
113
|
+
w.document.title = title;
|
|
114
|
+
w.document.body.style.margin = "0";
|
|
115
|
+
w.document.body.style.background = background;
|
|
116
|
+
for (const inject of injectStyles)
|
|
117
|
+
inject(w.document);
|
|
118
|
+
return w;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Register a global keyboard shortcut that fires `onTrigger`.
|
|
122
|
+
*
|
|
123
|
+
* The listener attaches to `document` in a `useEffect` so it's browser-only
|
|
124
|
+
* and cleaned up on unmount.
|
|
125
|
+
*
|
|
126
|
+
* @param key - The key string (e.g. `"D"`) matched case-insensitively.
|
|
127
|
+
* @param modifiers - Which modifier keys must be held.
|
|
128
|
+
* @param onTrigger - `onTrigger` may be a fresh closure each render — its
|
|
129
|
+
* latest identity is always used (captured via ref).
|
|
130
|
+
*/
|
|
131
|
+
export const useToggleHotkey = (key, modifiers, onTrigger) => {
|
|
132
|
+
// Latest-value ref so the effect doesn't need onTrigger in its dep array —
|
|
133
|
+
// avoids re-attaching the listener when the callback identity changes.
|
|
134
|
+
const cbRef = useRef(onTrigger);
|
|
135
|
+
cbRef.current = onTrigger;
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
const handler = (e) => {
|
|
138
|
+
if (modifiers.shift && !e.shiftKey)
|
|
139
|
+
return;
|
|
140
|
+
if (modifiers.alt && !e.altKey)
|
|
141
|
+
return;
|
|
142
|
+
if (modifiers.ctrl && !e.ctrlKey)
|
|
143
|
+
return;
|
|
144
|
+
if (modifiers.meta && !e.metaKey)
|
|
145
|
+
return;
|
|
146
|
+
if (e.key.toLowerCase() !== key.toLowerCase())
|
|
147
|
+
return;
|
|
148
|
+
e.preventDefault();
|
|
149
|
+
cbRef.current();
|
|
150
|
+
};
|
|
151
|
+
// document access inside useEffect — browser-only (C5/C6).
|
|
152
|
+
document.addEventListener("keydown", handler);
|
|
153
|
+
return () => document.removeEventListener("keydown", handler);
|
|
154
|
+
}, [key, modifiers.shift, modifiers.alt, modifiers.ctrl, modifiers.meta]);
|
|
155
|
+
};
|
|
156
|
+
//# sourceMappingURL=chrome.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chrome.js","sourceRoot":"","sources":["../src/chrome.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type DiffResult, ReplayPlayer } from "@reactra/replay";
|
|
3
|
+
import type { SessionBundle } from "@reactra/behaviours/replayable";
|
|
4
|
+
/**
|
|
5
|
+
* The active compare session: two loaded bundles, their diff, the currently
|
|
6
|
+
* selected component instance, and the selected divergence ordinal.
|
|
7
|
+
*/
|
|
8
|
+
export interface CompareSession {
|
|
9
|
+
bundleA: SessionBundle;
|
|
10
|
+
bundleB: SessionBundle;
|
|
11
|
+
playerA: ReplayPlayer;
|
|
12
|
+
playerB: ReplayPlayer;
|
|
13
|
+
diff: DiffResult;
|
|
14
|
+
/** The selected componentId (full, with `#N` suffix). */
|
|
15
|
+
componentId: string;
|
|
16
|
+
/** The ordinal index (into `stops`) of the selected divergence row. -1 = none. */
|
|
17
|
+
selectedOrdinal: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Props for the compare-mode body. `panel.ts` owns all state mutations and
|
|
21
|
+
* passes down callbacks so this module stays pure.
|
|
22
|
+
*/
|
|
23
|
+
export interface CompareBodyProps {
|
|
24
|
+
session: CompareSession;
|
|
25
|
+
onSelectComponent: (id: string) => void;
|
|
26
|
+
onSelectOrdinal: (ordinal: number) => void;
|
|
27
|
+
onExit: () => void;
|
|
28
|
+
}
|
|
29
|
+
/** Render the compare-mode panel body. */
|
|
30
|
+
export declare const CompareBody: (props: CompareBodyProps) => ReactNode;
|
|
31
|
+
//# sourceMappingURL=compare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"AAOA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAC1D,OAAO,EACL,KAAK,UAAU,EAGf,YAAY,EACb,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAInE;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,aAAa,CAAA;IACtB,OAAO,EAAE,aAAa,CAAA;IACtB,OAAO,EAAE,YAAY,CAAA;IACrB,OAAO,EAAE,YAAY,CAAA;IACrB,IAAI,EAAE,UAAU,CAAA;IAChB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,eAAe,EAAE,MAAM,CAAA;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,cAAc,CAAA;IACvB,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB;AAED,0CAA0C;AAC1C,eAAO,MAAM,WAAW,GAAI,OAAO,gBAAgB,KAAG,SAmIrD,CAAA"}
|
package/dist/compare.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// @reactra/replay-devtools — compare-mode render module.
|
|
2
|
+
//
|
|
3
|
+
// Owner: reactra-runtime-spec.md §3 (UI-companion tier). Consumed by panel.ts,
|
|
4
|
+
// which owns all state — this module is pure rendering (no hooks, no state
|
|
5
|
+
// setters except those passed in). Per the UI-companion tier rules: createElement
|
|
6
|
+
// not JSX, no .css imports, no Bun-only APIs, browser globals in handlers only.
|
|
7
|
+
import { createElement as h } from "react";
|
|
8
|
+
import { ReplayPlayer, } from "@reactra/replay";
|
|
9
|
+
import { formatOffset } from "./helpers.js";
|
|
10
|
+
import { Timeline } from "./timeline.js";
|
|
11
|
+
/** Render the compare-mode panel body. */
|
|
12
|
+
export const CompareBody = (props) => {
|
|
13
|
+
const { session, onSelectComponent, onSelectOrdinal, onExit } = props;
|
|
14
|
+
const { bundleA, bundleB, playerA, playerB, diff, componentId, selectedOrdinal } = session;
|
|
15
|
+
const inst = diff.instances.find((i) => i.componentId === componentId) ?? null;
|
|
16
|
+
// Component-picker options: every id present in BOTH bundles.
|
|
17
|
+
const sharedIds = diff.instances
|
|
18
|
+
.filter((i) => i.presence === "both")
|
|
19
|
+
.map((i) => i.componentId);
|
|
20
|
+
// All instance ids (including onlyA/onlyB) for the full list.
|
|
21
|
+
const allIds = diff.instances.map((i) => i.componentId);
|
|
22
|
+
// The two players' playhead positions for the selected ordinal.
|
|
23
|
+
const selectedStop = inst?.stops[selectedOrdinal] ?? null;
|
|
24
|
+
const offsetA = selectedStop?.offsetA ?? null;
|
|
25
|
+
const offsetB = selectedStop?.offsetB ?? null;
|
|
26
|
+
// Use statesUpToIndex (not statesAt) so the displayed state is the exact
|
|
27
|
+
// snapshot the diff engine saw — avoids same-timestamp ambiguity (B1 fix).
|
|
28
|
+
const stateAtA = selectedStop !== null
|
|
29
|
+
? (playerA.statesUpToIndex(selectedStop.globalIndexA).get(componentId) ?? null)
|
|
30
|
+
: null;
|
|
31
|
+
const stateAtB = selectedStop !== null
|
|
32
|
+
? (playerB.statesUpToIndex(selectedStop.globalIndexB).get(componentId) ?? null)
|
|
33
|
+
: null;
|
|
34
|
+
// --- Divergence list ---
|
|
35
|
+
const divergenceList = renderDivergenceList(inst, selectedOrdinal, onSelectOrdinal);
|
|
36
|
+
// --- Dual timelines (ordinal-locked playheads) ---
|
|
37
|
+
const timelineA = h(Timeline, {
|
|
38
|
+
bundle: bundleA,
|
|
39
|
+
offset: offsetA ?? 0,
|
|
40
|
+
window: null,
|
|
41
|
+
visible: () => true,
|
|
42
|
+
// Ordinal-locked — playhead position is driven by divergence list selection only.
|
|
43
|
+
onScrub: () => { },
|
|
44
|
+
onWindowChange: () => { },
|
|
45
|
+
onGrab: () => { },
|
|
46
|
+
});
|
|
47
|
+
const timelineB = h(Timeline, {
|
|
48
|
+
bundle: bundleB,
|
|
49
|
+
offset: offsetB ?? 0,
|
|
50
|
+
window: null,
|
|
51
|
+
visible: () => true,
|
|
52
|
+
onScrub: () => { },
|
|
53
|
+
onWindowChange: () => { },
|
|
54
|
+
onGrab: () => { },
|
|
55
|
+
});
|
|
56
|
+
// --- State cards for the selected ordinal ---
|
|
57
|
+
const statePair = (stateAtA || stateAtB) && renderStatePair(stateAtA, stateAtB, selectedStop, componentId);
|
|
58
|
+
// --- onlyA / onlyB instances (COND-1: never silently dropped) ---
|
|
59
|
+
const presenceNote = renderPresenceNotes(diff, componentId);
|
|
60
|
+
return h("div", { className: "rrd-compare" },
|
|
61
|
+
// Header bar: component picker + A/B session ids + exit
|
|
62
|
+
h("div", { className: "rrd-compare-head" }, h("span", { className: "rrd-compare-label" }, "comparing"), h("code", { className: "rrd-compare-sid" }, bundleA.sessionId.slice(0, 8)), h("span", { className: "rrd-compare-vs" }, "⟷"), h("code", { className: "rrd-compare-sid" }, bundleB.sessionId.slice(0, 8)), h("select", {
|
|
63
|
+
className: "rrd-select",
|
|
64
|
+
value: componentId,
|
|
65
|
+
onChange: (e) => onSelectComponent(e.target.value),
|
|
66
|
+
title: "Pick the instance to compare",
|
|
67
|
+
},
|
|
68
|
+
// Shared instances first (they have real diffs), then onlyA/B
|
|
69
|
+
...allIds.map((id) => {
|
|
70
|
+
const presence = diff.instances.find((i) => i.componentId === id)?.presence;
|
|
71
|
+
const label = presence === "both" ? id : `${id} (${presence})`;
|
|
72
|
+
return h("option", { key: id, value: id }, label);
|
|
73
|
+
})), h("span", { className: "rrd-spacer" }), h("button", { className: "rrd-btn rrd-mini", onClick: onExit, title: "Exit compare mode" }, "✕ exit compare")),
|
|
74
|
+
// Alignment note
|
|
75
|
+
h("div", { className: "rrd-compare-note" }, "aligned by event ordinal (v1)"), presenceNote,
|
|
76
|
+
// Main split: divergence list | right panel (timelines + state cards)
|
|
77
|
+
h("div", { className: "rrd-compare-body" },
|
|
78
|
+
// Left: divergence list
|
|
79
|
+
h("div", { className: "rrd-compare-left" }, divergenceList,
|
|
80
|
+
// COND-1: unpaired tail — visible, not silently dropped
|
|
81
|
+
inst && renderUnpairedTail(inst, bundleA, bundleB)),
|
|
82
|
+
// Right: dual timelines + side-by-side state cards
|
|
83
|
+
h("div", { className: "rrd-compare-right" }, sharedIds.includes(componentId) && inst && inst.stops.length > 0
|
|
84
|
+
? h("div", { className: "rrd-compare-timelines" }, h("div", { className: "rrd-compare-track-label" }, `A · ${bundleA.sessionId.slice(0, 8)}`), timelineA, h("div", { className: "rrd-compare-track-label" }, `B · ${bundleB.sessionId.slice(0, 8)}`), timelineB)
|
|
85
|
+
: null, statePair)));
|
|
86
|
+
};
|
|
87
|
+
/** Divergence list: each row is one ordinal with at least one key diff. */
|
|
88
|
+
const renderDivergenceList = (inst, selectedOrdinal, onSelectOrdinal) => {
|
|
89
|
+
if (!inst)
|
|
90
|
+
return null;
|
|
91
|
+
if (inst.presence !== "both") {
|
|
92
|
+
return h("div", { className: "rrd-compare-divlist" }, h("div", { className: "rrd-hint" }, inst.presence === "onlyA"
|
|
93
|
+
? "Instance only in session A — no ordinal alignment possible."
|
|
94
|
+
: "Instance only in session B — no ordinal alignment possible."));
|
|
95
|
+
}
|
|
96
|
+
const divergentStops = inst.stops.filter((s) => s.changedKeys.length > 0);
|
|
97
|
+
const actionDivs = [
|
|
98
|
+
...inst.actionsOnlyA.map((name, i) => h("div", { key: `a-action-${i}`, className: "rrd-compare-row rrd-compare-row--event" }, h("span", { className: "rrd-compare-ordinal" }, "–"), h("span", { className: "rrd-compare-key" }, `action: ${name}`), h("span", { className: "rrd-compare-aside" }, "A only"))),
|
|
99
|
+
...inst.actionsOnlyB.map((name, i) => h("div", { key: `b-action-${i}`, className: "rrd-compare-row rrd-compare-row--event" }, h("span", { className: "rrd-compare-ordinal" }, "–"), h("span", { className: "rrd-compare-key" }, `action: ${name}`), h("span", { className: "rrd-compare-aside" }, "B only"))),
|
|
100
|
+
...inst.resourcesOnlyA.map((name, i) => h("div", { key: `a-res-${i}`, className: "rrd-compare-row rrd-compare-row--event" }, h("span", { className: "rrd-compare-ordinal" }, "–"), h("span", { className: "rrd-compare-key" }, `resource: ${name}`), h("span", { className: "rrd-compare-aside" }, "A only"))),
|
|
101
|
+
...inst.resourcesOnlyB.map((name, i) => h("div", { key: `b-res-${i}`, className: "rrd-compare-row rrd-compare-row--event" }, h("span", { className: "rrd-compare-ordinal" }, "–"), h("span", { className: "rrd-compare-key" }, `resource: ${name}`), h("span", { className: "rrd-compare-aside" }, "B only"))),
|
|
102
|
+
];
|
|
103
|
+
if (divergentStops.length === 0 && actionDivs.length === 0) {
|
|
104
|
+
return h("div", { className: "rrd-compare-divlist" }, h("div", { className: "rrd-compare-agree" }, `No divergences — agree across ${inst.stops.length} aligned event${inst.stops.length === 1 ? "" : "s"}.`));
|
|
105
|
+
}
|
|
106
|
+
return h("div", { className: "rrd-compare-divlist" }, h("div", { className: "rrd-compare-divlist-head" }, `DIVERGENCES (${divergentStops.length + actionDivs.length})`),
|
|
107
|
+
// State divergences
|
|
108
|
+
...divergentStops.flatMap((stop) => stop.changedKeys.map((kd) => h("div", {
|
|
109
|
+
key: `${stop.ordinal}-${kd.key}`,
|
|
110
|
+
className: stop.ordinal === selectedOrdinal
|
|
111
|
+
? "rrd-compare-row rrd-compare-row--selected"
|
|
112
|
+
: "rrd-compare-row",
|
|
113
|
+
onClick: () => onSelectOrdinal(stop.ordinal),
|
|
114
|
+
}, h("span", { className: "rrd-compare-ordinal" }, `#${stop.ordinal}`), h("span", { className: "rrd-compare-key" }, kd.key), h("span", { className: "rrd-compare-ab" }, h("span", { className: "rrd-compare-val rrd-compare-val--a" }, JSON.stringify(kd.valueA)), h("span", { className: "rrd-compare-neq" }, "≠"), h("span", { className: "rrd-compare-val rrd-compare-val--b" }, JSON.stringify(kd.valueB)))))),
|
|
115
|
+
// Event-level divergences
|
|
116
|
+
...actionDivs);
|
|
117
|
+
};
|
|
118
|
+
/** COND-1: render the unpaired tail as visible "session A ends here" markers. */
|
|
119
|
+
const renderUnpairedTail = (inst, bundleA, bundleB) => {
|
|
120
|
+
const items = [];
|
|
121
|
+
if (inst.unpairedA.length > 0) {
|
|
122
|
+
items.push(h("div", { key: "unpairedA", className: "rrd-compare-unpaired" }, `session A continues (${inst.unpairedA.length} more stop${inst.unpairedA.length === 1 ? "" : "s"} — `, inst.unpairedA.map((off) => formatOffset(off)).join(", "), " — B ended at ordinal ", inst.stops.length - 1, ")"));
|
|
123
|
+
}
|
|
124
|
+
if (inst.unpairedB.length > 0) {
|
|
125
|
+
items.push(h("div", { key: "unpairedB", className: "rrd-compare-unpaired" }, `session B continues (${inst.unpairedB.length} more stop${inst.unpairedB.length === 1 ? "" : "s"} — `, inst.unpairedB.map((off) => formatOffset(off)).join(", "), " — A ended at ordinal ", inst.stops.length - 1, ")"));
|
|
126
|
+
}
|
|
127
|
+
return items.length > 0 ? h("div", { className: "rrd-compare-tail" }, ...items) : null;
|
|
128
|
+
};
|
|
129
|
+
/** Render side-by-side state cards for the selected ordinal (COND-1: amber diffs). */
|
|
130
|
+
const renderStatePair = (stateA, stateB, stop, componentId) => {
|
|
131
|
+
const changedSet = new Set(stop?.changedKeys.map((k) => k.key) ?? []);
|
|
132
|
+
const allKeys = new Set([
|
|
133
|
+
...Object.keys(stateA ?? {}),
|
|
134
|
+
...Object.keys(stateB ?? {}),
|
|
135
|
+
]);
|
|
136
|
+
const renderCard = (label, state, side) => h("div", { className: `rrd-compare-card rrd-compare-card--${side}` }, h("div", { className: "rrd-compare-card-head" }, h("span", { className: "rrd-compare-card-label" }, label), h("code", { className: "rrd-compare-card-id" }, componentId)), state
|
|
137
|
+
? h("div", { className: "rrd-compare-kv-list" }, ...[...allKeys].map((k) => h("div", { key: k, className: "rrd-kv" }, h("span", { className: "rrd-k" }, `${k}:`), h("span", {
|
|
138
|
+
className: changedSet.has(k) ? "rrd-v rrd-v--changed" : "rrd-v",
|
|
139
|
+
}, k in (state ?? {}) ? JSON.stringify(state[k]) : h("em", null, "absent")))))
|
|
140
|
+
: h("div", { className: "rrd-hint" }, "no state at this ordinal"));
|
|
141
|
+
if (!stop) {
|
|
142
|
+
return h("div", { className: "rrd-compare-cards" }, h("div", { className: "rrd-hint" }, "select a divergence row to inspect state"));
|
|
143
|
+
}
|
|
144
|
+
return h("div", { className: "rrd-compare-cards" }, h("div", { className: "rrd-compare-cards-head" }, `ordinal ${stop.ordinal} · A: ${formatOffset(stop.offsetA)} · B: ${formatOffset(stop.offsetB)}`), h("div", { className: "rrd-compare-cards-row" }, renderCard("A", stateA, "a"), renderCard("B", stateB, "b")));
|
|
145
|
+
};
|
|
146
|
+
/** COND-1: note instances that are only in one bundle. */
|
|
147
|
+
const renderPresenceNotes = (diff, currentId) => {
|
|
148
|
+
const onlyA = diff.instances.filter((i) => i.presence === "onlyA");
|
|
149
|
+
const onlyB = diff.instances.filter((i) => i.presence === "onlyB");
|
|
150
|
+
const noShared = diff.instances.every((i) => i.presence !== "both");
|
|
151
|
+
if (noShared) {
|
|
152
|
+
return h("div", { className: "rrd-compare-notice rrd-compare-notice--warn" }, "These sessions share no comparable instance.");
|
|
153
|
+
}
|
|
154
|
+
const items = [];
|
|
155
|
+
for (const i of onlyA) {
|
|
156
|
+
items.push(h("div", { key: `oa-${i.componentId}`, className: "rrd-compare-notice" }, h("code", null, i.componentId), " only in session A"));
|
|
157
|
+
}
|
|
158
|
+
for (const i of onlyB) {
|
|
159
|
+
items.push(h("div", { key: `ob-${i.componentId}`, className: "rrd-compare-notice" }, h("code", null, i.componentId), " only in session B"));
|
|
160
|
+
}
|
|
161
|
+
return items.length > 0
|
|
162
|
+
? h("div", { className: "rrd-compare-notices" }, ...items)
|
|
163
|
+
: null;
|
|
164
|
+
};
|
|
165
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"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"}
|