feat(browser): Action markers in timeline of session replay#1092
feat(browser): Action markers in timeline of session replay#1092
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Invalid CSS pseudo-class selector silently ignored
- Replaced the invalid
:first-child-of-typeselector with valid:first-of-typeso the first timeline lane correctly has no top margin.
- Replaced the invalid
Or push these changes by commenting:
@cursor push 86dc152142
Preview (86dc152142)
diff --git a/src/views/Validator/Browser/SessionPlayer/Timeline/TimelineActions.tsx b/src/views/Validator/Browser/SessionPlayer/Timeline/TimelineActions.tsx
--- a/src/views/Validator/Browser/SessionPlayer/Timeline/TimelineActions.tsx
+++ b/src/views/Validator/Browser/SessionPlayer/Timeline/TimelineActions.tsx
@@ -212,7 +212,7 @@
margin-top: 1px;
- &:first-child-of-type {
+ &:first-of-type {
margin-top: 0;
}
`}
@Llandy3d Could you check the console (i.e. in the devtools, not the console in the debugger) for errors? Something like not being able to find elements. |
Yeah, I want to add it too, but I don't want to bloat this PR with it. |
| }: TimelineTooltipProps) { | ||
| return ( | ||
| <Popover.Root open={open}> | ||
| <Popover.Trigger>{children}</Popover.Trigger> |
There was a problem hiding this comment.
Missing asChild on Popover.Trigger nests buttons
High Severity
The Popover.Trigger component renders a <button> by default, and its children is also a <button>. This creates invalid nested buttons in the DOM, which leads to unpredictable click handling. Specifically, the inner button's event handlers may not fire, breaking seeking functionality when clicking action markers.
Reviewed by Cursor Bugbot for commit febdd4c. Configure here.
There was a problem hiding this comment.
Bugbot Autofix determined this is a false positive.
The Radix UI Themes Popover.Trigger already uses asChild internally by default, so it merges with the child button element rather than wrapping it, preventing nested buttons.
You can send follow-ups to the cloud agent here.
going-confetti
left a comment
There was a problem hiding this comment.
Looks good and works well! The patch fixed the issues I discovered last time I reviewed this branch 🎉 . I'm still experiencing an issues with navigating to individual segments in some cases (see video attached to one of the comments) - not sure if it's related to the specific test.
| top: 0; | ||
| height: 100%; | ||
| background: white; | ||
| opacity: 0.3; |
| return ( | ||
| <div | ||
| css={css` | ||
| padding-bottom: 6px; |
There was a problem hiding this comment.
Is the extra space under the tracks reserved for something?
There was a problem hiding this comment.
Yes, the timeline itself. Without it you don't have a surface to seek by clicking.
| } | ||
|
|
||
| onSeek({ time: newTime, commit: true }) | ||
| const handleSeek = (seekTime: number, commit: boolean) => { |
There was a problem hiding this comment.
(Not sure if this is the right place in code, but it's easier to reply to this kind of comments rather than to standalone ones, so here we are)
I've had some issues trying to navigate to a segment by clicking on it in the timeline:
Screen.Recording.2026-04-08.at.11.38.06.mov
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Aborted action-end replay events are never generated
- Changed line 91 to filter session.actions instead of the transformed actions array, allowing action-end events to be correctly generated for in-flight actions.
- ✅ Fixed: Playback state never transitions to ended after streaming
- Added setState('ended') call in the recording-end handler to explicitly transition playback state after stopLive() pauses the player.
Or push these changes by commenting:
@cursor push bdcd30495d
Preview (bdcd30495d)
diff --git a/package-lock.json b/package-lock.json
--- a/package-lock.json
+++ b/package-lock.json
@@ -331,6 +331,7 @@
"version": "7.24.9",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz",
"integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==",
+ "peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.24.7",
@@ -733,6 +734,7 @@
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@dnd-kit/accessibility": "^3.1.1",
"@dnd-kit/utilities": "^3.2.2",
@@ -1258,6 +1260,7 @@
"integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"chalk": "^4.1.1",
"fs-extra": "^9.0.1",
@@ -1700,6 +1703,7 @@
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.13.5",
@@ -2662,6 +2666,7 @@
"integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@inquirer/checkbox": "^3.0.1",
"@inquirer/confirm": "^4.0.1",
@@ -2998,6 +3003,7 @@
"integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.1.0",
@@ -3308,6 +3314,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"license": "Apache-2.0",
+ "peer": true,
"engines": {
"node": ">=8.0.0"
}
@@ -3329,6 +3336,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz",
"integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==",
"license": "Apache-2.0",
+ "peer": true,
"engines": {
"node": ">=14"
},
@@ -3365,6 +3373,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz",
"integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"@opentelemetry/api-logs": "0.57.2",
"@types/shimmer": "^1.2.0",
@@ -3895,6 +3904,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.31.0.tgz",
"integrity": "sha512-cYJeP+6qN0UnBv1r09hXl0YorB8kXHv61BC0NUlBA8vxrylZ4/C8lnva3gd1E8n33DNYSaiGW+DuGoSt0QQ7Dw==",
"license": "Apache-2.0",
+ "peer": true,
"engines": {
"node": ">=14"
}
@@ -6948,6 +6958,7 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.0.tgz",
"integrity": "sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"undici-types": "~6.21.0"
}
@@ -7024,6 +7035,7 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz",
"integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -7034,6 +7046,7 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true,
"license": "MIT",
+ "peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -7138,6 +7151,7 @@
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.1.tgz",
"integrity": "sha512-SxdPak/5bO0EnGktV05+Hq8oatjAYVY3Zh2bye9pGZy6+jwyR3LG3YKkV4YatlsgqXP28BTeVm9pqwJM96vf2A==",
"dev": true,
+ "peer": true,
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "7.16.1",
@@ -8024,6 +8038,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -9226,6 +9241,7 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -11445,16 +11461,6 @@
"node": ">= 0.8"
}
},
- "node_modules/encoding": {
- "version": "0.1.13",
- "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
- "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "iconv-lite": "^0.6.2"
- }
- },
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -11805,6 +11811,7 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
"dev": true,
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -11943,6 +11950,7 @@
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",
"integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==",
"dev": true,
+ "peer": true,
"dependencies": {
"array-includes": "^3.1.7",
"array.prototype.findlastindex": "^1.2.3",
@@ -13840,6 +13848,7 @@
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
+ "peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@@ -16065,6 +16074,7 @@
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
"integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"dompurify": "3.2.7",
"marked": "14.0.0"
@@ -18050,6 +18060,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -18074,6 +18085,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -18085,6 +18097,7 @@
"version": "7.53.0",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.0.tgz",
"integrity": "sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==",
+ "peer": true,
"engines": {
"node": ">=18.0.0"
},
@@ -19037,6 +19050,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -20546,7 +20560,8 @@
"node_modules/tslib": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
- "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
+ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
+ "peer": true
},
"node_modules/type-check": {
"version": "0.4.0",
@@ -20701,6 +20716,7 @@
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
"integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -21308,6 +21324,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
@@ -22430,6 +22447,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
+ "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/src/hooks/useBrowserSession.ts b/src/hooks/useBrowserSession.ts
--- a/src/hooks/useBrowserSession.ts
+++ b/src/hooks/useBrowserSession.ts
@@ -88,7 +88,7 @@
})
// Insert action end events for all actions that were running when the script stopped.
- const actionEnds = actions
+ const actionEnds = session.actions
.filter((action) => action.type === 'begin')
.map((action) =>
createReplayEvent({
diff --git a/src/views/Validator/Browser/SessionPlayer/SessionPlayer.hooks.ts b/src/views/Validator/Browser/SessionPlayer/SessionPlayer.hooks.ts
--- a/src/views/Validator/Browser/SessionPlayer/SessionPlayer.hooks.ts
+++ b/src/views/Validator/Browser/SessionPlayer/SessionPlayer.hooks.ts
@@ -111,6 +111,7 @@
case 'recording-end':
newPlayer.stopLive()
newPlayer.setConfig({ liveMode: false })
+ setState('ended')
breakYou can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Double recording-end events on user-initiated stop
- Removed the immediate ScriptHandler.Stopped send from the Stop IPC handler, keeping only the event that fires after the process actually terminates.
- ✅ Fixed: Unused exported
ActionBeginEventtype in rrweb.ts- Removed the unused ActionBeginEvent type export from rrweb.ts since all code uses the ActionBeginEvent type from schema.ts instead.
Or push these changes by commenting:
@cursor push 9312cf5f20
Preview (9312cf5f20)
diff --git a/package-lock.json b/package-lock.json
--- a/package-lock.json
+++ b/package-lock.json
@@ -331,6 +331,7 @@
"version": "7.24.9",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz",
"integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==",
+ "peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.24.7",
@@ -733,6 +734,7 @@
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@dnd-kit/accessibility": "^3.1.1",
"@dnd-kit/utilities": "^3.2.2",
@@ -1258,6 +1260,7 @@
"integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"chalk": "^4.1.1",
"fs-extra": "^9.0.1",
@@ -1700,6 +1703,7 @@
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.13.5",
@@ -2662,6 +2666,7 @@
"integrity": "sha512-yl43JD/86CIj3Mz5mvvLJqAOfIup7ncxfJ0Btnl0/v5TouVUyeEdcpknfgc+yMevS/48oH9WAkkw93m7otLb/A==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@inquirer/checkbox": "^3.0.1",
"@inquirer/confirm": "^4.0.1",
@@ -2998,6 +3003,7 @@
"integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.1.0",
@@ -3308,6 +3314,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"license": "Apache-2.0",
+ "peer": true,
"engines": {
"node": ">=8.0.0"
}
@@ -3329,6 +3336,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz",
"integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==",
"license": "Apache-2.0",
+ "peer": true,
"engines": {
"node": ">=14"
},
@@ -3365,6 +3373,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz",
"integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"@opentelemetry/api-logs": "0.57.2",
"@types/shimmer": "^1.2.0",
@@ -3895,6 +3904,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.31.0.tgz",
"integrity": "sha512-cYJeP+6qN0UnBv1r09hXl0YorB8kXHv61BC0NUlBA8vxrylZ4/C8lnva3gd1E8n33DNYSaiGW+DuGoSt0QQ7Dw==",
"license": "Apache-2.0",
+ "peer": true,
"engines": {
"node": ">=14"
}
@@ -6948,6 +6958,7 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.0.tgz",
"integrity": "sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"undici-types": "~6.21.0"
}
@@ -7024,6 +7035,7 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz",
"integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -7034,6 +7046,7 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true,
"license": "MIT",
+ "peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -7138,6 +7151,7 @@
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.1.tgz",
"integrity": "sha512-SxdPak/5bO0EnGktV05+Hq8oatjAYVY3Zh2bye9pGZy6+jwyR3LG3YKkV4YatlsgqXP28BTeVm9pqwJM96vf2A==",
"dev": true,
+ "peer": true,
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "7.16.1",
@@ -8024,6 +8038,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -9226,6 +9241,7 @@
}
],
"license": "MIT",
+ "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -11445,16 +11461,6 @@
"node": ">= 0.8"
}
},
- "node_modules/encoding": {
- "version": "0.1.13",
- "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
- "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "iconv-lite": "^0.6.2"
- }
- },
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -11805,6 +11811,7 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
"dev": true,
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -11943,6 +11950,7 @@
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",
"integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==",
"dev": true,
+ "peer": true,
"dependencies": {
"array-includes": "^3.1.7",
"array.prototype.findlastindex": "^1.2.3",
@@ -13840,6 +13848,7 @@
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
+ "peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@@ -16065,6 +16074,7 @@
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
"integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"dompurify": "3.2.7",
"marked": "14.0.0"
@@ -18050,6 +18060,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -18074,6 +18085,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -18085,6 +18097,7 @@
"version": "7.53.0",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.0.tgz",
"integrity": "sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==",
+ "peer": true,
"engines": {
"node": ">=18.0.0"
},
@@ -19037,6 +19050,7 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -20546,7 +20560,8 @@
"node_modules/tslib": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
- "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
+ "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
+ "peer": true
},
"node_modules/type-check": {
"version": "0.4.0",
@@ -20701,6 +20716,7 @@
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
"integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -21308,6 +21324,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
@@ -22430,6 +22447,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
+ "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/src/handlers/script/index.ts b/src/handlers/script/index.ts
--- a/src/handlers/script/index.ts
+++ b/src/handlers/script/index.ts
@@ -83,7 +83,7 @@
})
})
- ipcMain.on(ScriptHandler.Stop, (event) => {
+ ipcMain.on(ScriptHandler.Stop, () => {
console.info(`${ScriptHandler.Stop} event received`)
if (currentTestRun) {
currentTestRun.stop().catch((error) => {
@@ -92,9 +92,6 @@
currentTestRun = null
}
-
- const browserWindow = browserWindowFromEvent(event)
- browserWindow.webContents.send(ScriptHandler.Stopped)
})
ipcMain.handle(
diff --git a/src/main/runner/rrweb.ts b/src/main/runner/rrweb.ts
--- a/src/main/runner/rrweb.ts
+++ b/src/main/runner/rrweb.ts
@@ -47,7 +47,6 @@
export type RecordingEndEvent = z.infer<typeof RecordingEndEventSchema>
export type PageStartEvent = z.infer<typeof PageStartEventSchema>
-export type ActionBeginEvent = z.infer<typeof ActionBeginEventSchema>
export type CustomReplayEvent = z.infer<typeof CustomReplayEventSchema>You can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Segment clicks inside slider cause competing seek operations
- Added stopPropagation() to handlePointerDown and handleClick handlers in the segment button to prevent events from bubbling up to the slider.
Or push these changes by commenting:
@cursor push 256e39edcf
Preview (256e39edcf)
diff --git a/src/views/Validator/Browser/SessionPlayer/Timeline/TimelineActions.tsx b/src/views/Validator/Browser/SessionPlayer/Timeline/TimelineActions.tsx
--- a/src/views/Validator/Browser/SessionPlayer/Timeline/TimelineActions.tsx
+++ b/src/views/Validator/Browser/SessionPlayer/Timeline/TimelineActions.tsx
@@ -98,10 +98,12 @@
const status = segment.action.result?.type ?? 'unknown'
const handlePointerDown = (ev: MouseEvent<HTMLElement>) => {
+ ev.stopPropagation()
mouseDownX.current = ev.clientX
}
const handleClick = (ev: MouseEvent<HTMLElement>) => {
+ ev.stopPropagation()
// We want to allow users to drag the timeline so we only register the click
// if the mouse hasn't moved by more than a few pixels.
const deltaX = ev.clientX - (mouseDownX.current ?? ev.clientX)You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit ed668e4. Configure here.
| if (Math.abs(deltaX) < 10) { | ||
| onSeek(segment.action.timestamp.started) | ||
| } | ||
| } |
There was a problem hiding this comment.
Segment clicks inside slider cause competing seek operations
Medium Severity
Segment buttons are rendered inside SliderPrimitive.Track, so pointer events on segments bubble up to the slider root. Clicking a segment triggers both the slider's seek (to the click position on the track via onValueChange/onValueCommit) and the segment's own seek (to segment.action.timestamp.started). These competing seeks cause the player to first jump to the wrong position, then to the intended position, producing visual glitches. The handlePointerDown and handleClick handlers on the segment button don't call stopPropagation() to prevent the slider from also processing the events.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit ed668e4. Configure here.




Description
This PR adds action markers in the timeline of the debugger.
Screen.Recording.2026-03-13.at.18.09.47.mov
Each action can be hovered and clicking it will seek to the beginning of that action. If two or more actions are happening in parallel they will be shown in different lanes to indicate this.
How to Test
Checklist
Related PR(s)/Issue(s)
Resolves #1064
Note
Medium Risk
Adds new replay/timeline behavior and relies on a
patch-packagemonkey patch ofrrweblive-mode behavior, which could affect debugger playback/stop semantics. Main risk is regressions in session stopping/seeking and replay rendering across rrweb updates.Overview
Adds action markers to the session replay timeline: the playback slider is replaced with a custom
TimelineSliderthat renders action-duration segments (stacked into lanes for overlaps), supports hover tooltips, and lets users click a marker to seek to that action.Refactors browser debug data collection into a single
useBrowserSessionhook that tracks actions and replay together and injects custom rrweb events (action-begin/action-end/recording-end) with explicit timestamps, including aborting in-flight actions on stop.Introduces
patch-package(with a newpostinstall) and a patch torrwebaddingReplayer.stopLive()/STOP_LIVE, which is used to cleanly exit live mode when arecording-endevent is received; also removes older stop-time replay injection from the main process.Reviewed by Cursor Bugbot for commit ed668e4. Bugbot is set up for automated code reviews on this repo. Configure here.