Skip to content

Commit 54b2333

Browse files
authored
fix(analytics): stop session replay canvas capture eating CPU (#3138)
1 parent 619e5ae commit 54b2333

4 files changed

Lines changed: 24 additions & 0 deletions

File tree

packages/ui/src/features/terminal/TerminalManager.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ describe("TerminalManager.destroyForTask", () => {
258258
terminalManager.detach("sess-parked");
259259

260260
const parking = document.getElementById("terminal-parking");
261+
expect(parking?.classList.contains("ph-no-capture")).toBe(true);
261262
expect(parking?.childElementCount).toBe(1);
262263

263264
terminalManager.destroy("sess-parked");

packages/ui/src/features/terminal/TerminalManager.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ function getParkingContainer(): HTMLElement {
2121
if (!parkingContainer) {
2222
parkingContainer = document.createElement("div");
2323
parkingContainer.id = "terminal-parking";
24+
// Parked terminals keep live WebGL canvases; ph-no-capture stops PostHog
25+
// session replay snapshotting every one of them at canvasFps forever.
26+
parkingContainer.className = "ph-no-capture";
2427
parkingContainer.style.position = "absolute";
2528
parkingContainer.style.visibility = "hidden";
2629
parkingContainer.style.pointerEvents = "none";

packages/ui/src/shell/posthogAnalyticsImpl.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,19 @@ describe("initializePostHog", () => {
192192
expect(mockPosthog.onFeatureFlags).not.toHaveBeenCalled();
193193
});
194194

195+
it("disables replay canvas capture regardless of remote config", async () => {
196+
const { initializePostHog } = await loadAnalytics();
197+
198+
initializePostHog();
199+
200+
expect(mockPosthog.init).toHaveBeenCalledWith(
201+
"test-key",
202+
expect.objectContaining({
203+
session_recording: { captureCanvas: { recordCanvas: false } },
204+
}),
205+
);
206+
});
207+
195208
it("bootstraps posthog with the main-owned session id", async () => {
196209
const { initializePostHog } = await loadAnalytics();
197210

packages/ui/src/shell/posthogAnalyticsImpl.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ export function initializePostHog(sessionId?: string) {
7272
// and rely on explicit instrumentation instead.
7373
capture_pageview: false,
7474
disable_session_recording: false,
75+
// Never let the project's remote canvasRecording config apply here: our
76+
// canvases are xterm terminals (secrets show up as pixels, bypassing text
77+
// masking) and decorative WebGL (hedgehog, confetti). Capturing them costs
78+
// an image encode per canvas at canvasFps for the whole app lifetime.
79+
session_recording: {
80+
captureCanvas: { recordCanvas: false },
81+
},
7582
session_idle_timeout_seconds: SESSION_IDLE_TIMEOUT_SECONDS,
7683
...(sessionId ? { bootstrap: { sessionID: sessionId } } : {}),
7784
capture_exceptions: import.meta.env.DEV

0 commit comments

Comments
 (0)