Skip to content

Commit e07d0bc

Browse files
committed
feat: notifications
1 parent fe4e17d commit e07d0bc

34 files changed

+7184
-272
lines changed

Diff for: biome.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"enabled": true
2424
},
2525
"linter": {
26-
"enabled": true,
26+
"enabled": false,
2727
"rules": {
2828
"recommended": true,
2929
"correctness": {

Diff for: packages/scan/src/core/index.ts

+82-32
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import type {
2424
} from './instrumentation';
2525
import type { InternalInteraction } from './monitor/types';
2626
import type { getSession } from './monitor/utils';
27+
import { startTimingTracking } from './notifications/event-tracking';
28+
import { createHighlightCanvas } from './notifications/outline-overlay';
2729

2830
let rootContainer: HTMLDivElement | null = null;
2931
let shadowRoot: ShadowRoot | null = null;
@@ -160,6 +162,22 @@ export interface Options {
160162
*/
161163
trackUnnecessaryRenders?: boolean;
162164

165+
/**
166+
* Should the FPS meter show in the toolbar
167+
*
168+
* @default true
169+
*/
170+
showFPS?: boolean;
171+
172+
/**
173+
* Should react scan log internal errors to the console.
174+
*
175+
* Useful if react scan is not behaving expected and you want to provide information to maintainers when submitting an issue https://github.com/aidenybai/react-scan/issues
176+
*
177+
* @default false
178+
*/
179+
_debug?: 'verbose' | false;
180+
163181
onCommitStart?: () => void;
164182
onRender?: (fiber: Fiber, renders: Array<Render>) => void;
165183
onCommitFinish?: () => void;
@@ -197,6 +215,9 @@ export interface StoreType {
197215
fiberRoots: WeakSet<Fiber>;
198216
reportData: Map<number, RenderData>;
199217
legacyReportData: Map<string, RenderData>;
218+
interactionListeningForRenders:
219+
| ((fiber: Fiber, renders: Array<Render>) => void)
220+
| null;
200221
}
201222

202223
export type OutlineKey = `${string}-${string}`;
@@ -270,6 +291,7 @@ export const Store: StoreType = {
270291
reportData: new Map<number, RenderData>(),
271292
legacyReportData: new Map<string, RenderData>(),
272293
lastReportTime: signal(0),
294+
interactionListeningForRenders: null,
273295
};
274296

275297
export const ReactScanInternals: Internals = {
@@ -286,6 +308,7 @@ export const ReactScanInternals: Internals = {
286308
// alwaysShowLabels: false,
287309
animationSpeed: 'fast',
288310
dangerouslyForceRunInProduction: false,
311+
showFPS: true,
289312
// smoothlyAnimateOutlines: true,
290313
// trackUnnecessaryRenders: false,
291314
}),
@@ -465,50 +488,62 @@ export const getIsProduction = () => {
465488
};
466489

467490
export const start = () => {
468-
if (typeof window === 'undefined') {
469-
return;
470-
}
491+
try {
492+
if (typeof window === 'undefined') {
493+
return;
494+
}
471495

472-
if (
473-
getIsProduction() &&
474-
!ReactScanInternals.options.value.dangerouslyForceRunInProduction
475-
) {
476-
return;
477-
}
496+
if (
497+
getIsProduction() &&
498+
!ReactScanInternals.options.value.dangerouslyForceRunInProduction
499+
) {
500+
return;
501+
}
478502

479-
const localStorageOptions =
480-
readLocalStorage<LocalStorageOptions>('react-scan-options');
503+
const localStorageOptions =
504+
readLocalStorage<LocalStorageOptions>('react-scan-options');
481505

482-
if (localStorageOptions) {
483-
const validLocalOptions = validateOptions(localStorageOptions);
506+
if (localStorageOptions) {
507+
const validLocalOptions = validateOptions(localStorageOptions);
484508

485-
if (Object.keys(validLocalOptions).length > 0) {
486-
ReactScanInternals.options.value = {
487-
...ReactScanInternals.options.value,
488-
...validLocalOptions,
489-
};
509+
if (Object.keys(validLocalOptions).length > 0) {
510+
ReactScanInternals.options.value = {
511+
...ReactScanInternals.options.value,
512+
...validLocalOptions,
513+
};
514+
}
490515
}
491-
}
492516

493-
const options = getOptions();
494-
495-
initReactScanInstrumentation(() => {
496-
initToolbar(!!options.value.showToolbar);
497-
});
498-
499-
const isUsedInBrowserExtension = typeof window !== 'undefined';
500-
if (!Store.monitor.value && !isUsedInBrowserExtension) {
501-
setTimeout(() => {
502-
if (isInstrumentationActive()) return;
503-
// biome-ignore lint/suspicious/noConsole: Intended debug output
517+
const options = getOptions();
518+
519+
initReactScanInstrumentation(() => {
520+
initToolbar(!!options.value.showToolbar);
521+
});
522+
523+
const isUsedInBrowserExtension = typeof window !== 'undefined';
524+
if (!Store.monitor.value && !isUsedInBrowserExtension) {
525+
setTimeout(() => {
526+
if (isInstrumentationActive()) return;
527+
// biome-ignore lint/suspicious/noConsole: Intended debug output
528+
console.error(
529+
'[React Scan] Failed to load. Must import React Scan before React runs.',
530+
);
531+
}, 5000);
532+
}
533+
} catch (e) {
534+
if (ReactScanInternals.options.value._debug === 'verbose') {
504535
console.error(
505-
'[React Scan] Failed to load. Must import React Scan before React runs.',
536+
'[React Scan Internal Error]',
537+
'Failed to create notifications outline canvas',
538+
e,
506539
);
507-
}, 5000);
540+
}
508541
}
509542
};
510543

511544
const initToolbar = (showToolbar: boolean) => {
545+
startTimingTracking();
546+
createNotificationsOutlineCanvas();
512547
const windowToolbarContainer = window.__REACT_SCAN_TOOLBAR_CONTAINER__;
513548

514549
if (!showToolbar) {
@@ -521,6 +556,21 @@ const initToolbar = (showToolbar: boolean) => {
521556
createToolbar(shadowRoot);
522557
};
523558

559+
const createNotificationsOutlineCanvas = () => {
560+
try {
561+
const highlightRoot = document.documentElement;
562+
createHighlightCanvas(highlightRoot);
563+
} catch (e) {
564+
if (ReactScanInternals.options.value._debug === 'verbose') {
565+
console.error(
566+
'[React Scan Internal Error]',
567+
'Failed to create notifications outline canvas',
568+
e,
569+
);
570+
}
571+
}
572+
};
573+
524574
export const scan = (options: Options = {}) => {
525575
setOptions(options);
526576
const isInIframe = Store.isInIframe.value;

Diff for: packages/scan/src/core/monitor/utils.ts

+16
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,19 @@ export const getSession = async ({
131131
cachedSession = session;
132132
return session;
133133
};
134+
135+
136+
137+
export const not_globally_unique_generateId = () => {
138+
if (typeof window === 'undefined') {
139+
return '0';
140+
}
141+
142+
// @ts-expect-error
143+
if (window.reactScanIdCounter === undefined) {
144+
// @ts-expect-error
145+
window.reactScanIdCounter = 0;
146+
}
147+
// @ts-expect-error
148+
return `${++window.reactScanIdCounter}`;
149+
};

0 commit comments

Comments
 (0)