Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6671e27
feat(extension): add network recording for BrowserStack LT integration
nafees87n May 28, 2026
182280b
refactor(extension): emit network recording events as HAR 1.2 entries
nafees87n May 29, 2026
77cdec9
feat(extension): stream network recording entries to LTS over a port
nafees87n May 29, 2026
09bcf56
feat(extension): stop returns summary; { action, payload } external e…
nafees87n May 29, 2026
ed05a92
feat(extension): Firefox sidebar for network recording panel
nafees87n May 29, 2026
2a2bc02
feat(extension): keep the service worker alive during a network recor…
nafees87n May 29, 2026
56f3668
fix(extension): address network-recording code review findings
nafees87n May 29, 2026
5d817db
feat(extension): add getNetworkRecordingSummary for stream consumers
nafees87n Jun 1, 2026
23003d8
feat(extension): network panel UX — host display, scroll, chip wrap
nafees87n Jun 1, 2026
0c02db8
refactor(extension): finalize stop/complete/summary contract
nafees87n Jun 1, 2026
c47400f
Merge branch 'master' into feat/network-recording-har
nafees87n Jun 1, 2026
f42ef06
refactor(extension): drop unreachable fetch resourceType branch
nafees87n Jun 1, 2026
b1c5865
chore(extension): log received network entries to console in test har…
nafees87n Jun 2, 2026
e3138ec
feat(extension): network-recording lifecycle — focus-return, disconne…
nafees87n Jun 2, 2026
0197339
feat(extension): guard network recording on extension-enabled state
nafees87n Jun 3, 2026
21c86fe
refactor(extension): drop chrome.alarms from network recording
nafees87n Jun 3, 2026
e5f2bcc
fix(extension): decouple network-recording panel open from tab creation
nafees87n Jun 3, 2026
53d9d75
fix(extension): restore eager side-panel open on network recording start
nafees87n Jun 3, 2026
a1cae4e
feat(extension): reject network recording start when extension is dis…
nafees87n Jun 3, 2026
b87bd7b
fix(extension): catch first-ever disable toggle for network recording
nafees87n Jun 3, 2026
8bce6fe
fix(extension): don't route user away on extension-disabled stop
nafees87n Jun 3, 2026
db11d14
refactor(extension): route user back only on user-initiated stop
nafees87n Jun 3, 2026
8a95ecc
feat(extension): Network Interceptor v2 — request/response bodies + h…
nafees87n Jun 3, 2026
eacad9c
fix(extension): network recording — dedupe SDK re-injection + determi…
nafees87n Jun 4, 2026
f545cde
feat(extension): make network-recording fallback URL configurable via…
nafees87n Jun 8, 2026
103365f
chore(extension): raise default network-recording body cap to 200 KB
nafees87n Jun 8, 2026
153ea57
feat(extension): floating reopen widget for network-recording side pa…
nafees87n Jun 8, 2026
327078d
refactor(extension): drop "Recording" label from network-recording wi…
nafees87n Jun 8, 2026
4698ee9
Merge branch 'master' into feat/network-recording-har
nafees87n Jun 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions browser-extension/common/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,36 @@ export default [
},
plugins: [...commonPlugins, nodeResolve()],
},
{
...commonConfig,
input: "src/sidepanel/network-recording/index.tsx",
output: {
file: `${OUTPUT_DIR}/sidepanel/network-recording/index.js`,
format: "iife",
},
context: "window",
plugins: [
copy({
targets: [
{
src: "src/sidepanel/network-recording/index.html",
dest: `${OUTPUT_DIR}/sidepanel/network-recording`,
},
],
}),
nodeResolve(),
replace({
preventAssignment: true,
"process.env.NODE_ENV": JSON.stringify("production"),
}),
...commonPlugins,
commonjs(),
postcss({
extract: true,
}),
svgr(),
],
},
{
...commonConfig,
input: "src/custom-elements/index.ts",
Expand Down
16 changes: 16 additions & 0 deletions browser-extension/common/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,20 @@ export const EXTENSION_MESSAGES = {
DESKTOP_APP_CONNECTION_STATUS_UPDATED: "desktopAppConnectionStatusUpdated",
IS_SESSION_REPLAY_ENABLED: "isSessionReplayEnabled",
TRIGGER_OPEN_CURL_MODAL: "triggerOpenCurlModal",
STOP_NETWORK_RECORDING: "stopNetworkRecording",
GET_NETWORK_RECORDING_STATE: "getNetworkRecordingState",
// v2 body capture: SW → client content script → page script (networkBodyRecorder) start/stop.
START_NETWORK_BODY_CAPTURE: "startNetworkBodyCapture",
STOP_NETWORK_BODY_CAPTURE: "stopNetworkBodyCapture",
// Floating widget: content script → SW request to reopen the closed side panel.
REOPEN_NETWORK_RECORDING_PANEL: "reopenNetworkRecordingPanel",
};

export const EXTENSION_EXTERNAL_MESSAGES = {
GET_EXTENSION_METADATA: "getExtensionMetadata",
START_NETWORK_RECORDING: "startNetworkRecording",
STOP_NETWORK_RECORDING: "stopNetworkRecording",
GET_NETWORK_RECORDING_SUMMARY: "getNetworkRecordingSummary",
};

export const CLIENT_MESSAGES = {
Expand All @@ -72,6 +82,12 @@ export const CLIENT_MESSAGES = {
NOTIFY_RECORD_UPDATED: "notifyRecordUpdated",
NOTIFY_EXTENSION_STATUS_UPDATED: "notifyExtensionStatusUpdated",
OPEN_CURL_IMPORT_MODAL: "openCurlImportModal",
NETWORK_EVENT_CAPTURED: "networkEventCaptured",
NETWORK_RECORDING_ENDED: "networkRecordingEnded",
NETWORK_BODY_CAPTURED: "networkBodyCaptured",
// Floating widget: SW → content script to show/hide the on-page reopen widget.
SHOW_NETWORK_RECORDING_WIDGET: "showNetworkRecordingWidget",
HIDE_NETWORK_RECORDING_WIDGET: "hideNetworkRecordingWidget",
};

export const STORAGE_TYPE = "local";
Expand Down
1 change: 1 addition & 0 deletions browser-extension/common/src/custom-elements/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ import "./test-rule-widget/explicit-test-rule-widget";
import "./test-rule-widget/implicit-test-rule-widget";
import "./session-recording-widgets/draft-session-viewer";
import "./session-recording-widgets/post-session-save-widget";
import "./network-recording-widget";
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
:host {
position: fixed !important;
z-index: 2147483647 !important;
/* Shrink-wrap to content so the pill keeps its shape at the viewport edge instead of being
squeezed by the fixed-position box (which caused the label/button to wrap). */
width: max-content !important;
max-width: max-content !important;
}

#container {
display: none;
align-items: center;
gap: 8px;
background: #212121;
color: #fff;
border: 1px solid #333;
border-radius: 8px;
padding: 8px 12px;
font-family: system-ui, -apple-system, sans-serif;
font-size: 13px;
line-height: 1.2;
white-space: nowrap;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
cursor: grab;
user-select: none;
}

#container.visible {
display: flex;
}

.recording-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #e43434;
flex: none;
animation: rq-nr-blink 1s cubic-bezier(0.5, 0, 1, 1) infinite alternate;
}

@keyframes rq-nr-blink {
from {
opacity: 1;
}
to {
opacity: 0.3;
}
}

.reopen {
cursor: pointer;
color: #4caf50;
font-weight: 600;
padding: 2px 8px;
border-radius: 4px;
flex: none;
white-space: nowrap;
}

.reopen:hover {
background: rgba(76, 175, 80, 0.15);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import styles from "./index.css";
import { registerCustomElement, setInnerHTML } from "../utils";
import { RQDraggableWidget } from "../abstract-classes/draggable-widget";

/**
* Minimal floating widget shown on a recorded tab WHEN the network-recording side panel is closed.
* Lets the user reopen the panel (click → "reopen" event → content script → SW → sidePanel.open).
* Top-right, draggable. Enrich later (status counts etc.) once the reopen path is verified.
*/

enum RQNetworkRecordingWidgetEvent {
REOPEN = "reopen",
}

const TAG_NAME = "rq-network-recording-widget";
const DEFAULT_POSITION = { top: 16, right: 16 };

class RQNetworkRecordingWidget extends RQDraggableWidget {
constructor() {
super(DEFAULT_POSITION);
this.shadowRoot = this.attachShadow({ mode: "closed" });
setInnerHTML(this.shadowRoot, this._getDefaultMarkup());

this.show = this.show.bind(this);
this.hide = this.hide.bind(this);
}

connectedCallback() {
super.connectedCallback();
this.addListeners();
// Show on connect (like the session-recording widget). The element is only created when we
// want it shown, and this avoids a race where the content script dispatches "show" before
// this listener is registered (custom-element upgrade is async after createElement/append).
this.show();
}

addListeners() {
this.shadowRoot.querySelector(".reopen").addEventListener("click", (evt) => {
evt.stopPropagation();
this.dispatchEvent(new CustomEvent(RQNetworkRecordingWidgetEvent.REOPEN));
});

this.addEventListener("show", (evt: CustomEvent) => this.show(evt.detail?.position));
this.addEventListener("hide", this.hide);
}

_getDefaultMarkup() {
return `
<style>${styles}</style>
<div id="container">
<span class="recording-dot" title="Recording"></span>
<span class="reopen" title="Reopen the recording panel">Open panel</span>
</div>
`;
}

show(position = DEFAULT_POSITION) {
this.moveToPostion(position);
this.setAttribute("draggable", "true");
this.shadowRoot.querySelector("#container").classList.add("visible");
}

hide() {
this.shadowRoot.querySelector("#container").classList.remove("visible");
}
}

registerCustomElement(TAG_NAME, RQNetworkRecordingWidget);
Loading