diff --git a/packages/replay/src/constants.ts b/packages/replay/src/constants.ts index 13ccea43df22..ec3325a4253a 100644 --- a/packages/replay/src/constants.ts +++ b/packages/replay/src/constants.ts @@ -53,3 +53,33 @@ export const MAX_REPLAY_DURATION = 3_600_000; // 60 minutes in ms; /** Default attributes to be ignored when `maskAllText` is enabled */ export const DEFAULT_IGNORED_ATTRIBUTES = ['title', 'placeholder']; + +export const CANVAS_QUALITY = { + low: { + sampling: { + canvas: 1, + }, + dataURLOptions: { + type: 'image/webp', + quality: 0.25, + }, + }, + medium: { + sampling: { + canvas: 2, + }, + dataURLOptions: { + type: 'image/webp', + quality: 0.4, + }, + }, + high: { + sampling: { + canvas: 4, + }, + dataURLOptions: { + type: 'image/webp', + quality: 0.5, + }, + }, +}; diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index d0dc3097bcf6..87c71dddec3e 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -6,6 +6,7 @@ import { logger } from '@sentry/utils'; import { BUFFER_CHECKOUT_TIME, + CANVAS_QUALITY, SESSION_IDLE_EXPIRE_DURATION, SESSION_IDLE_PAUSE_DURATION, SLOW_CLICK_SCROLL_TIMEOUT, @@ -340,15 +341,12 @@ export class ReplayContainer implements ReplayContainerInterface { ...(this.recordingMode === 'buffer' && { checkoutEveryNms: BUFFER_CHECKOUT_TIME }), emit: getHandleRecordingEmit(this), onMutation: this._onMutationHandler, - ...(canvas && { - recordCanvas: true, - sampling: { canvas: canvas.fps || 4 }, - dataURLOptions: { - type: canvas.type || 'image/webp', - quality: canvas.quality || 0.6, - }, - getCanvasManager: canvas.manager, - }), + ...(canvas && + canvas.manager && { + recordCanvas: true, + getCanvasManager: canvas.manager, + ...(CANVAS_QUALITY[canvas.quality || 'medium'] || CANVAS_QUALITY.medium), + }), }); } catch (err) { this._handleException(err); diff --git a/packages/replay/src/types/replay.ts b/packages/replay/src/types/replay.ts index fb1f91c0e1a9..1b2b19d4d4cd 100644 --- a/packages/replay/src/types/replay.ts +++ b/packages/replay/src/types/replay.ts @@ -233,9 +233,7 @@ export interface ReplayPluginOptions extends ReplayNetworkOptions { captureExceptions: boolean; traceInternals: boolean; canvas: { - fps?: number; - quality?: number; - type?: string; + quality?: 'low' | 'medium' | 'high'; manager: (options: GetCanvasManagerOptions) => CanvasManagerInterface; }; }>; diff --git a/packages/replay/test/integration/rrweb.test.ts b/packages/replay/test/integration/rrweb.test.ts index 82dd18f2d6ec..3543a6771ce4 100644 --- a/packages/replay/test/integration/rrweb.test.ts +++ b/packages/replay/test/integration/rrweb.test.ts @@ -1,3 +1,4 @@ +import type { CanvasManagerInterface } from '../../src/types'; import { resetSdkMock } from '../mocks/resetSdkMock'; import { useFakeTimers } from '../utils/use-fake-timers'; @@ -40,4 +41,33 @@ describe('Integration | rrweb', () => { } `); }); + + it('calls rrweb.record with default canvas options', async () => { + const { mockRecord } = await resetSdkMock({ + replayOptions: { + _experiments: { + canvas: { + // @ts-expect-error This should return + // CanvasManagerInterface, but we don't care about it + // for this test + manager: () => null, + }, + }, + }, + }); + + expect(mockRecord).toHaveBeenLastCalledWith( + expect.objectContaining({ + recordCanvas: true, + getCanvasManager: expect.any(Function), + dataURLOptions: { + quality: 0.4, + type: 'image/webp', + }, + sampling: { + canvas: 2, + }, + }), + ); + }); });