From 96829c1a53e687e74acb1f77191b63d5aa91e15b Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Mon, 18 Nov 2024 17:54:32 +0100 Subject: [PATCH 01/17] fix(trace-updates): add component name display to canvas --- .../src/backend/views/TraceUpdates/canvas.js | 52 ++++++++++++------- .../src/backend/views/TraceUpdates/index.js | 7 +++ 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js index c4475155943..a20db7e1c5e 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js @@ -14,8 +14,6 @@ import type Agent from '../../agent'; import {isReactNativeEnvironment} from 'react-devtools-shared/src/backend/utils'; -const OUTLINE_COLOR = '#f0f0f0'; - // Note these colors are in sync with DevTools Profiler chart colors. const COLORS = [ '#37afa9', @@ -52,9 +50,12 @@ function drawWeb(nodeToData: Map) { const context = canvasFlow.getContext('2d'); context.clearRect(0, 0, canvasFlow.width, canvasFlow.height); - iterateNodes(nodeToData, (rect, color) => { + iterateNodes(nodeToData, (rect, color, displayName) => { if (rect !== null) { drawBorder(context, rect, color); + if (displayName !== null) { + drawComponentName(context, rect, displayName, color); + } } }); } @@ -67,12 +68,12 @@ export function draw(nodeToData: Map, agent: Agent): void { function iterateNodes( nodeToData: Map, - execute: (rect: Rect | null, color: string, node: HostInstance) => void, + execute: (rect: Rect | null, color: string, displayName: string | null) => void, ) { - nodeToData.forEach(({count, rect}, node) => { + nodeToData.forEach(({count, rect, displayName}, node) => { const colorIndex = Math.min(COLORS.length - 1, count - 1); const color = COLORS[colorIndex]; - execute(rect, color, node); + execute(rect, color, displayName); }); } @@ -83,25 +84,38 @@ function drawBorder( ): void { const {height, left, top, width} = rect; - // outline + // border + context.strokeStyle = color; context.lineWidth = 1; - context.strokeStyle = OUTLINE_COLOR; + context.strokeRect(left, top, width - 1, height - 1); +} - context.strokeRect(left - 1, top - 1, width + 2, height + 2); +function drawComponentName( + context: CanvasRenderingContext2D, + rect: Rect, + displayName: string, + color: string, +): void { + const {left, top} = rect; - // inset - context.lineWidth = 1; - context.strokeStyle = OUTLINE_COLOR; - context.strokeRect(left + 1, top + 1, width - 1, height - 1); - context.strokeStyle = color; + context.font = '10px monospace'; + context.textBaseline = 'middle'; + context.textAlign = 'center'; - context.setLineDash([0]); + const metrics = context.measureText(displayName); + const padding = 2; + const textHeight = 14; + const backgroundWidth = metrics.width + padding * 2; + const backgroundHeight = textHeight; - // border - context.lineWidth = 1; - context.strokeRect(left, top, width - 1, height - 1); + const labelX = left; + const labelY = top - backgroundHeight; + + context.fillStyle = color; + context.fillRect(labelX, labelY, backgroundWidth, backgroundHeight); - context.setLineDash([0]); + context.fillStyle = '#000000'; + context.fillText(displayName, labelX + (backgroundWidth / 2), labelY + (backgroundHeight / 2)); } function destroyNative(agent: Agent) { diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js index 2f27a941b56..b7cf466adb6 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js @@ -36,6 +36,7 @@ export type Data = { expirationTime: number, lastMeasuredAt: number, rect: Rect | null, + displayName: string | null, }; const nodeToData: Map = new Map(); @@ -86,6 +87,11 @@ function traceUpdates(nodes: Set): void { rect = measureNode(node); } + let displayName = agent.getComponentNameForHostInstance(node); + if (displayName != null && displayName.startsWith('Forget(')) { + displayName = '✨' + displayName.slice(7, -1); + } + nodeToData.set(node, { count: data != null ? data.count + 1 : 1, expirationTime: @@ -97,6 +103,7 @@ function traceUpdates(nodes: Set): void { : now + DISPLAY_DURATION, lastMeasuredAt, rect, + displayName, }); }); From c8de4d5a1e877aff833d381e1153654ecd03037e Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Mon, 18 Nov 2024 17:54:32 +0100 Subject: [PATCH 02/17] fix(trace-updates): improve canvas rendering on high-DPI screens --- .../src/backend/views/TraceUpdates/canvas.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js index a20db7e1c5e..f15914d49f2 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js @@ -45,11 +45,18 @@ function drawWeb(nodeToData: Map) { } const canvasFlow: HTMLCanvasElement = ((canvas: any): HTMLCanvasElement); - canvasFlow.width = window.innerWidth; - canvasFlow.height = window.innerHeight; + const dpr = window.devicePixelRatio || 1; + + canvasFlow.width = window.innerWidth * dpr; + canvasFlow.height = window.innerHeight * dpr; + + canvasFlow.style.width = `${window.innerWidth}px`; + canvasFlow.style.height = `${window.innerHeight}px`; const context = canvasFlow.getContext('2d'); - context.clearRect(0, 0, canvasFlow.width, canvasFlow.height); + context.scale(dpr, dpr); + + context.clearRect(0, 0, window.innerWidth, window.innerHeight); iterateNodes(nodeToData, (rect, color, displayName) => { if (rect !== null) { drawBorder(context, rect, color); From 273287a4f3be1497a27fdcb831267324cd44a30d Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Mon, 18 Nov 2024 17:54:32 +0100 Subject: [PATCH 03/17] refactor(trace-updates): simplify `iterateNodes` function and improve type consistency --- .../src/backend/views/TraceUpdates/canvas.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js index f15914d49f2..196f6904638 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js @@ -32,7 +32,7 @@ let canvas: HTMLCanvasElement | null = null; function drawNative(nodeToData: Map, agent: Agent) { const nodesToDraw = []; - iterateNodes(nodeToData, (_, color, node) => { + iterateNodes(nodeToData, ({ color, node }) => { nodesToDraw.push({node, color}); }); @@ -57,7 +57,7 @@ function drawWeb(nodeToData: Map) { context.scale(dpr, dpr); context.clearRect(0, 0, window.innerWidth, window.innerHeight); - iterateNodes(nodeToData, (rect, color, displayName) => { + iterateNodes(nodeToData, ({ rect, color, displayName }) => { if (rect !== null) { drawBorder(context, rect, color); if (displayName !== null) { @@ -73,14 +73,20 @@ export function draw(nodeToData: Map, agent: Agent): void { : drawWeb(nodeToData); } +type DataWithColorAndNode = { + ...Data, + color: string; + node: HostInstance; +}; + function iterateNodes( nodeToData: Map, - execute: (rect: Rect | null, color: string, displayName: string | null) => void, + execute: (data: DataWithColorAndNode) => void, ) { - nodeToData.forEach(({count, rect, displayName}, node) => { - const colorIndex = Math.min(COLORS.length - 1, count - 1); + nodeToData.forEach((data, node) => { + const colorIndex = Math.min(COLORS.length - 1, data.count - 1); const color = COLORS[colorIndex]; - execute(rect, color, displayName); + execute({...data, color, node}); }); } From 48d53414a2f23866f397087f857d960c3537361f Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Mon, 18 Nov 2024 17:54:32 +0100 Subject: [PATCH 04/17] feat(trace-updates): display update count on component labels --- .../src/backend/views/TraceUpdates/canvas.js | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js index 196f6904638..fea5e764933 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js @@ -32,7 +32,7 @@ let canvas: HTMLCanvasElement | null = null; function drawNative(nodeToData: Map, agent: Agent) { const nodesToDraw = []; - iterateNodes(nodeToData, ({ color, node }) => { + iterateNodes(nodeToData, ({color, node}) => { nodesToDraw.push({node, color}); }); @@ -57,11 +57,11 @@ function drawWeb(nodeToData: Map) { context.scale(dpr, dpr); context.clearRect(0, 0, window.innerWidth, window.innerHeight); - iterateNodes(nodeToData, ({ rect, color, displayName }) => { + iterateNodes(nodeToData, ({rect, color, displayName, count}) => { if (rect !== null) { drawBorder(context, rect, color); if (displayName !== null) { - drawComponentName(context, rect, displayName, color); + drawComponentName(context, rect, displayName, color, count); } } }); @@ -75,8 +75,8 @@ export function draw(nodeToData: Map, agent: Agent): void { type DataWithColorAndNode = { ...Data, - color: string; - node: HostInstance; + color: string, + node: HostInstance, }; function iterateNodes( @@ -108,14 +108,18 @@ function drawComponentName( rect: Rect, displayName: string, color: string, + count: number, ): void { const {left, top} = rect; + const countText = count > 1 ? ` x${count}` : ''; + const text = `${displayName}${countText}`; + context.font = '10px monospace'; context.textBaseline = 'middle'; context.textAlign = 'center'; - const metrics = context.measureText(displayName); + const metrics = context.measureText(text); const padding = 2; const textHeight = 14; const backgroundWidth = metrics.width + padding * 2; @@ -128,7 +132,11 @@ function drawComponentName( context.fillRect(labelX, labelY, backgroundWidth, backgroundHeight); context.fillStyle = '#000000'; - context.fillText(displayName, labelX + (backgroundWidth / 2), labelY + (backgroundHeight / 2)); + context.fillText( + text, + labelX + backgroundWidth / 2, + labelY + backgroundHeight / 2, + ); } function destroyNative(agent: Agent) { From 2982ccc4910f29df136feb58cd28841959b5793d Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Mon, 18 Nov 2024 17:54:32 +0100 Subject: [PATCH 05/17] feat(trace-updates): group overlapping updates and merge labels --- .../src/backend/views/TraceUpdates/canvas.js | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js index fea5e764933..6655e034619 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js @@ -57,12 +57,45 @@ function drawWeb(nodeToData: Map) { context.scale(dpr, dpr); context.clearRect(0, 0, window.innerWidth, window.innerHeight); - iterateNodes(nodeToData, ({rect, color, displayName, count}) => { + + type GroupItem = { + rect: Rect, + color: string, + displayName: string | null, + count: number, + }; + + const positionGroups: Map> = new Map(); + + iterateNodes(nodeToData, ({rect, color, displayName, count, node}) => { if (rect !== null) { + const key = `${rect.left},${rect.top}`; + const group = positionGroups.get(key) || []; + if (!positionGroups.has(key)) { + positionGroups.set(key, group); + } + group.push({rect, color, displayName, count}); + } + }); + + positionGroups.forEach(group => { + const {rect} = group[0]; + + group.forEach(({color}) => { drawBorder(context, rect, color); + }); + + const mergedName = group + .map(({displayName, count}) => { if (displayName !== null) { - drawComponentName(context, rect, displayName, color, count); + return `${displayName}${count > 1 ? ` x${count}` : ''}`; } + }) + .filter(Boolean) + .join(', '); + + if (mergedName) { + drawLabel(context, rect, mergedName, group[0].color); } }); } @@ -103,18 +136,14 @@ function drawBorder( context.strokeRect(left, top, width - 1, height - 1); } -function drawComponentName( +function drawLabel( context: CanvasRenderingContext2D, rect: Rect, - displayName: string, + text: string, color: string, - count: number, ): void { const {left, top} = rect; - const countText = count > 1 ? ` x${count}` : ''; - const text = `${displayName}${countText}`; - context.font = '10px monospace'; context.textBaseline = 'middle'; context.textAlign = 'center'; From 4aa2dc5e10103343ecfea32f0d0982db2655ada9 Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Mon, 18 Nov 2024 17:54:32 +0100 Subject: [PATCH 06/17] feat(trace-updates): sort component update labels by maximum update count --- .../src/backend/views/TraceUpdates/canvas.js | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js index 6655e034619..b0439502a53 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js @@ -78,26 +78,32 @@ function drawWeb(nodeToData: Map) { } }); - positionGroups.forEach(group => { - const {rect} = group[0]; - - group.forEach(({color}) => { - drawBorder(context, rect, color); - }); - - const mergedName = group - .map(({displayName, count}) => { - if (displayName !== null) { - return `${displayName}${count > 1 ? ` x${count}` : ''}`; + Array.from(positionGroups.entries()) + .sort(([, groupA], [, groupB]) => { + const maxCountA = Math.max(...groupA.map(item => item.count)); + const maxCountB = Math.max(...groupB.map(item => item.count)); + return maxCountA - maxCountB; + }) + .forEach(([_, group]) => { + const {rect} = group[0]; + + group.forEach(({color}) => { + drawBorder(context, rect, color); + }); + + const mergedName = group + .map(({displayName, count}) => { + if (displayName !== null) { + return `${displayName}${count > 1 ? ` x${count}` : ''}`; + } + }) + .filter(Boolean) + .join(', '); + + if (mergedName) { + drawLabel(context, rect, mergedName, group[0].color); } - }) - .filter(Boolean) - .join(', '); - - if (mergedName) { - drawLabel(context, rect, mergedName, group[0].color); - } - }); + }); } export function draw(nodeToData: Map, agent: Agent): void { From c4b71d70a3624bfa5e7faa5f2746a6f56c329f75 Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Mon, 18 Nov 2024 17:54:32 +0100 Subject: [PATCH 07/17] feat(trace-updates): add setting to show component names during trace highlighting --- .../react-devtools-shared/src/backend/agent.js | 12 ++++++++++++ .../src/backend/views/TraceUpdates/index.js | 11 ++++++++++- packages/react-devtools-shared/src/bridge.js | 1 + packages/react-devtools-shared/src/constants.js | 2 ++ .../devtools/views/Settings/GeneralSettings.js | 15 +++++++++++++++ .../devtools/views/Settings/SettingsContext.js | 16 ++++++++++++++++ .../devtools/views/Settings/SettingsShared.css | 11 +++++++++++ 7 files changed, 67 insertions(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/backend/agent.js b/packages/react-devtools-shared/src/backend/agent.js index e05abf51aba..63288984de1 100644 --- a/packages/react-devtools-shared/src/backend/agent.js +++ b/packages/react-devtools-shared/src/backend/agent.js @@ -146,6 +146,7 @@ export default class Agent extends EventEmitter<{ getIfHasUnsupportedRendererVersion: [], updateHookSettings: [$ReadOnly], getHookSettings: [], + showNamesWhenTracing: [boolean], }> { _bridge: BackendBridge; _isProfiling: boolean = false; @@ -156,6 +157,7 @@ export default class Agent extends EventEmitter<{ _onReloadAndProfile: | ((recordChangeDescriptions: boolean, recordTimeline: boolean) => void) | void; + _showNamesWhenTracing: boolean = true; constructor( bridge: BackendBridge, @@ -200,6 +202,7 @@ export default class Agent extends EventEmitter<{ bridge.addListener('reloadAndProfile', this.reloadAndProfile); bridge.addListener('renamePath', this.renamePath); bridge.addListener('setTraceUpdatesEnabled', this.setTraceUpdatesEnabled); + bridge.addListener('setShowNamesWhenTracing', this.setShowNamesWhenTracing); bridge.addListener('startProfiling', this.startProfiling); bridge.addListener('stopProfiling', this.stopProfiling); bridge.addListener('storeAsGlobal', this.storeAsGlobal); @@ -722,6 +725,7 @@ export default class Agent extends EventEmitter<{ this._traceUpdatesEnabled = traceUpdatesEnabled; setTraceUpdatesEnabled(traceUpdatesEnabled); + this.emit('showNamesWhenTracing', this._showNamesWhenTracing); for (const rendererID in this._rendererInterfaces) { const renderer = ((this._rendererInterfaces[ @@ -731,6 +735,14 @@ export default class Agent extends EventEmitter<{ } }; + setShowNamesWhenTracing: (show: boolean) => void = show => { + if (this._showNamesWhenTracing === show) { + return; + } + this._showNamesWhenTracing = show; + this.emit('showNamesWhenTracing', show); + }; + syncSelectionFromBuiltinElementsPanel: () => void = () => { const target = window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0; if (target == null) { diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js index b7cf466adb6..dac5f76b558 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js @@ -44,11 +44,20 @@ const nodeToData: Map = new Map(); let agent: Agent = ((null: any): Agent); let drawAnimationFrameID: AnimationFrameID | null = null; let isEnabled: boolean = false; +let showNames: boolean = false; let redrawTimeoutID: TimeoutID | null = null; export function initialize(injectedAgent: Agent): void { agent = injectedAgent; agent.addListener('traceUpdates', traceUpdates); + agent.addListener('showNamesWhenTracing', (shouldShowNames: boolean) => { + showNames = shouldShowNames; + if (isEnabled) { + if (drawAnimationFrameID === null) { + drawAnimationFrameID = requestAnimationFrame(prepareToDraw); + } + } + }); } export function toggleEnabled(value: boolean): void { @@ -103,7 +112,7 @@ function traceUpdates(nodes: Set): void { : now + DISPLAY_DURATION, lastMeasuredAt, rect, - displayName, + displayName: showNames ? displayName : null, }); }); diff --git a/packages/react-devtools-shared/src/bridge.js b/packages/react-devtools-shared/src/bridge.js index cb494e1b3c1..e00ba5518a1 100644 --- a/packages/react-devtools-shared/src/bridge.js +++ b/packages/react-devtools-shared/src/bridge.js @@ -234,6 +234,7 @@ type FrontendEvents = { renamePath: [RenamePath], savedPreferences: [SavedPreferencesParams], setTraceUpdatesEnabled: [boolean], + setShowNamesWhenTracing: [boolean], shutdown: [], startInspectingHost: [], startProfiling: [StartProfilingParams], diff --git a/packages/react-devtools-shared/src/constants.js b/packages/react-devtools-shared/src/constants.js index b0873816590..bb8d9f5b130 100644 --- a/packages/react-devtools-shared/src/constants.js +++ b/packages/react-devtools-shared/src/constants.js @@ -50,6 +50,8 @@ export const LOCAL_STORAGE_TRACE_UPDATES_ENABLED_KEY = 'React::DevTools::traceUpdatesEnabled'; export const LOCAL_STORAGE_SUPPORTS_PROFILING_KEY = 'React::DevTools::supportsProfiling'; +export const LOCAL_STORAGE_SHOW_NAMES_WHEN_TRACING_KEY = + 'React::DevTools::showNamesWhenTracing'; export const PROFILER_EXPORT_VERSION = 5; diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js b/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js index d56f32f8114..69f4ec73758 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js @@ -34,8 +34,10 @@ export default function GeneralSettings(_: {}): React.Node { setDisplayDensity, setTheme, setTraceUpdatesEnabled, + setShowNamesWhenTracing, theme, traceUpdatesEnabled, + showNamesWhenTracing, } = useContext(SettingsContext); const {backendVersion, supportsTraceUpdates} = useContext(StoreContext); @@ -83,6 +85,19 @@ export default function GeneralSettings(_: {}): React.Node { />{' '} Highlight updates when components render. +
+ +
)} diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js index 196ea806f6a..dbc70d224e4 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js @@ -21,6 +21,7 @@ import { LOCAL_STORAGE_BROWSER_THEME, LOCAL_STORAGE_PARSE_HOOK_NAMES_KEY, LOCAL_STORAGE_TRACE_UPDATES_ENABLED_KEY, + LOCAL_STORAGE_SHOW_NAMES_WHEN_TRACING_KEY, } from 'react-devtools-shared/src/constants'; import { COMFORTABLE_LINE_HEIGHT, @@ -53,6 +54,9 @@ type Context = { traceUpdatesEnabled: boolean, setTraceUpdatesEnabled: (value: boolean) => void, + + showNamesWhenTracing: boolean, + setShowNamesWhenTracing: (showNames: boolean) => void, }; const SettingsContext: ReactContext = createContext( @@ -111,6 +115,10 @@ function SettingsContextController({ LOCAL_STORAGE_TRACE_UPDATES_ENABLED_KEY, false, ); + const [showNamesWhenTracing, setShowNamesWhenTracing] = useLocalStorageWithLog( + LOCAL_STORAGE_SHOW_NAMES_WHEN_TRACING_KEY, + true, + ); const documentElements = useMemo(() => { const array: Array = [ @@ -164,6 +172,11 @@ function SettingsContextController({ bridge.send('setTraceUpdatesEnabled', traceUpdatesEnabled); }, [bridge, traceUpdatesEnabled]); + useEffect(() => { + console.log('sent to bridge: setShowNamesWhenTracing', showNamesWhenTracing); + bridge.send('setShowNamesWhenTracing', showNamesWhenTracing); + }, [bridge, showNamesWhenTracing]); + const value: Context = useMemo( () => ({ displayDensity, @@ -179,6 +192,8 @@ function SettingsContextController({ theme, browserTheme, traceUpdatesEnabled, + showNamesWhenTracing, + setShowNamesWhenTracing, }), [ displayDensity, @@ -190,6 +205,7 @@ function SettingsContextController({ theme, browserTheme, traceUpdatesEnabled, + showNamesWhenTracing, ], ); diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsShared.css b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsShared.css index c322bc8d0ea..ce27e83a3fa 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsShared.css +++ b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsShared.css @@ -154,3 +154,14 @@ padding: 0; margin: 0; } + +.Setting .Setting { + margin-left: 1rem; + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.Setting label:has(input:disabled) { + opacity: 0.5; + cursor: default; +} From 2f5c31edba342ac27538e949fa10b4e1df5bb605 Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Tue, 19 Nov 2024 00:56:47 +0100 Subject: [PATCH 08/17] refactor(trace-updates): improve draw logic and modularize canvas rendering --- .../src/backend/views/TraceUpdates/canvas.js | 120 +++++++++--------- .../views/Settings/SettingsContext.js | 14 +- 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js index b0439502a53..55b92311868 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js @@ -44,66 +44,78 @@ function drawWeb(nodeToData: Map) { initialize(); } - const canvasFlow: HTMLCanvasElement = ((canvas: any): HTMLCanvasElement); const dpr = window.devicePixelRatio || 1; - + const canvasFlow: HTMLCanvasElement = ((canvas: any): HTMLCanvasElement); canvasFlow.width = window.innerWidth * dpr; canvasFlow.height = window.innerHeight * dpr; - canvasFlow.style.width = `${window.innerWidth}px`; canvasFlow.style.height = `${window.innerHeight}px`; const context = canvasFlow.getContext('2d'); context.scale(dpr, dpr); - context.clearRect(0, 0, window.innerWidth, window.innerHeight); + context.clearRect(0, 0, canvasFlow.width / dpr, canvasFlow.height / dpr); + + const mergedNodes = groupAndSortNodes(nodeToData); - type GroupItem = { - rect: Rect, - color: string, - displayName: string | null, - count: number, - }; + mergedNodes.forEach(group => { + drawGroupBorders(context, group); + drawGroupLabel(context, group); + }); +} + +type GroupItem = { + rect: Rect, + color: string, + displayName: string | null, + count: number, +}; +export function groupAndSortNodes( + nodeToData: Map, +): Array> { const positionGroups: Map> = new Map(); - iterateNodes(nodeToData, ({rect, color, displayName, count, node}) => { - if (rect !== null) { - const key = `${rect.left},${rect.top}`; - const group = positionGroups.get(key) || []; - if (!positionGroups.has(key)) { - positionGroups.set(key, group); - } - group.push({rect, color, displayName, count}); - } + iterateNodes(nodeToData, ({rect, color, displayName, count}) => { + if (!rect) return; + const key = `${rect.left},${rect.top}`; + if (!positionGroups.has(key)) positionGroups.set(key, []); + positionGroups.get(key)?.push({rect, color, displayName, count}); + }); + + return Array.from(positionGroups.values()).sort((groupA, groupB) => { + const maxCountA = Math.max(...groupA.map(item => item.count)); + const maxCountB = Math.max(...groupB.map(item => item.count)); + return maxCountA - maxCountB; + }); +} + +function drawGroupBorders( + context: CanvasRenderingContext2D, + group: Array, +) { + group.forEach(({color, rect}) => { + context.beginPath(); + context.strokeStyle = color; + context.rect(rect.left, rect.top, rect.width - 1, rect.height - 1); + context.stroke(); }); +} - Array.from(positionGroups.entries()) - .sort(([, groupA], [, groupB]) => { - const maxCountA = Math.max(...groupA.map(item => item.count)); - const maxCountB = Math.max(...groupB.map(item => item.count)); - return maxCountA - maxCountB; - }) - .forEach(([_, group]) => { - const {rect} = group[0]; - - group.forEach(({color}) => { - drawBorder(context, rect, color); - }); - - const mergedName = group - .map(({displayName, count}) => { - if (displayName !== null) { - return `${displayName}${count > 1 ? ` x${count}` : ''}`; - } - }) - .filter(Boolean) - .join(', '); - - if (mergedName) { - drawLabel(context, rect, mergedName, group[0].color); - } - }); +function drawGroupLabel( + context: CanvasRenderingContext2D, + group: Array, +) { + const mergedName = group + .map(({displayName, count}) => + displayName ? `${displayName}${count > 1 ? ` x${count}` : ''}` : '', + ) + .filter(Boolean) + .join(', '); + + if (mergedName) { + drawLabel(context, group[0].rect, mergedName, group[0].color); + } } export function draw(nodeToData: Map, agent: Agent): void { @@ -129,19 +141,6 @@ function iterateNodes( }); } -function drawBorder( - context: CanvasRenderingContext2D, - rect: Rect, - color: string, -): void { - const {height, left, top, width} = rect; - - // border - context.strokeStyle = color; - context.lineWidth = 1; - context.strokeRect(left, top, width - 1, height - 1); -} - function drawLabel( context: CanvasRenderingContext2D, rect: Rect, @@ -149,17 +148,16 @@ function drawLabel( color: string, ): void { const {left, top} = rect; - context.font = '10px monospace'; context.textBaseline = 'middle'; context.textAlign = 'center'; - const metrics = context.measureText(text); const padding = 2; const textHeight = 14; + + const metrics = context.measureText(text); const backgroundWidth = metrics.width + padding * 2; const backgroundHeight = textHeight; - const labelX = left; const labelY = top - backgroundHeight; diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js index dbc70d224e4..06d3651aacc 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js @@ -115,10 +115,11 @@ function SettingsContextController({ LOCAL_STORAGE_TRACE_UPDATES_ENABLED_KEY, false, ); - const [showNamesWhenTracing, setShowNamesWhenTracing] = useLocalStorageWithLog( - LOCAL_STORAGE_SHOW_NAMES_WHEN_TRACING_KEY, - true, - ); + const [showNamesWhenTracing, setShowNamesWhenTracing] = + useLocalStorageWithLog( + LOCAL_STORAGE_SHOW_NAMES_WHEN_TRACING_KEY, + true, + ); const documentElements = useMemo(() => { const array: Array = [ @@ -173,7 +174,10 @@ function SettingsContextController({ }, [bridge, traceUpdatesEnabled]); useEffect(() => { - console.log('sent to bridge: setShowNamesWhenTracing', showNamesWhenTracing); + console.log( + 'sent to bridge: setShowNamesWhenTracing', + showNamesWhenTracing, + ); bridge.send('setShowNamesWhenTracing', showNamesWhenTracing); }, [bridge, showNamesWhenTracing]); From 547f4977415d69c44ba8242517927b36e83d7cd7 Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Tue, 19 Nov 2024 10:59:47 +0100 Subject: [PATCH 09/17] test(trace-updates): add tests for node grouping and sorting logic --- .../src/__tests__/traceUpdates-test.js | 269 ++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 packages/react-devtools-shared/src/__tests__/traceUpdates-test.js diff --git a/packages/react-devtools-shared/src/__tests__/traceUpdates-test.js b/packages/react-devtools-shared/src/__tests__/traceUpdates-test.js new file mode 100644 index 00000000000..b8aa978a149 --- /dev/null +++ b/packages/react-devtools-shared/src/__tests__/traceUpdates-test.js @@ -0,0 +1,269 @@ +import {groupAndSortNodes} from 'react-devtools-shared/src/backend/views/TraceUpdates/canvas'; + +describe('Trace updates group and sort nodes', () => { + test('should group nodes by position without changing order within group', () => { + const nodeToData = new Map([ + [ + {id: 1}, + { + rect: {left: 0, top: 0, width: 100, height: 100}, + color: '#80b393', + displayName: 'Node1', + count: 3, + }, + ], + [ + {id: 2}, + { + rect: {left: 0, top: 0, width: 100, height: 100}, + color: '#63b19e', + displayName: 'Node2', + count: 2, + }, + ], + ]); + + const result = groupAndSortNodes(nodeToData); + + expect(result).toEqual([ + [ + { + rect: {left: 0, top: 0, width: 100, height: 100}, + color: '#80b393', + displayName: 'Node1', + count: 3, + }, + { + rect: {left: 0, top: 0, width: 100, height: 100}, + color: '#63b19e', + displayName: 'Node2', + count: 2, + }, + ], + ]); + }); + + test('should sort groups by lowest count in each group', () => { + const nodeToData = new Map([ + [ + {id: 1}, + { + rect: {left: 0, top: 0, width: 100, height: 100}, + color: '#97b488', + displayName: 'Group1', + count: 4, + }, + ], + [ + {id: 2}, + { + rect: {left: 100, top: 0, width: 100, height: 100}, + color: '#37afa9', + displayName: 'Group2', + count: 1, + }, + ], + [ + {id: 3}, + { + rect: {left: 200, top: 0, width: 100, height: 100}, + color: '#63b19e', + displayName: 'Group3', + count: 2, + }, + ], + ]); + + const result = groupAndSortNodes(nodeToData); + + expect(result).toEqual([ + [ + { + rect: {left: 100, top: 0, width: 100, height: 100}, + color: '#37afa9', + displayName: 'Group2', + count: 1, + }, + ], + [ + { + rect: {left: 200, top: 0, width: 100, height: 100}, + color: '#63b19e', + displayName: 'Group3', + count: 2, + }, + ], + [ + { + rect: {left: 0, top: 0, width: 100, height: 100}, + color: '#97b488', + displayName: 'Group1', + count: 4, + }, + ], + ]); + }); + + test('should maintain order within groups while sorting groups by lowest count', () => { + const nodeToData = new Map([ + [ + {id: 1}, + { + rect: {left: 0, top: 0, width: 50, height: 50}, + color: '#97b488', + displayName: 'Pos1Node1', + count: 4, + }, + ], + [ + {id: 2}, + { + rect: {left: 0, top: 0, width: 60, height: 60}, + color: '#63b19e', + displayName: 'Pos1Node2', + count: 2, + }, + ], + [ + {id: 3}, + { + rect: {left: 100, top: 0, width: 70, height: 70}, + color: '#80b393', + displayName: 'Pos2Node1', + count: 3, + }, + ], + [ + {id: 4}, + { + rect: {left: 100, top: 0, width: 80, height: 80}, + color: '#37afa9', + displayName: 'Pos2Node2', + count: 1, + }, + ], + ]); + + const result = groupAndSortNodes(nodeToData); + + expect(result).toEqual([ + [ + { + rect: {left: 100, top: 0, width: 70, height: 70}, + color: '#80b393', + displayName: 'Pos2Node1', + count: 3, + }, + { + rect: {left: 100, top: 0, width: 80, height: 80}, + color: '#37afa9', + displayName: 'Pos2Node2', + count: 1, + }, + ], + [ + { + rect: {left: 0, top: 0, width: 50, height: 50}, + color: '#97b488', + displayName: 'Pos1Node1', + count: 4, + }, + { + rect: {left: 0, top: 0, width: 60, height: 60}, + color: '#63b19e', + displayName: 'Pos1Node2', + count: 2, + }, + ], + ]); + }); + + test('should handle multiple groups with same minimum count', () => { + const nodeToData = new Map([ + [ + {id: 1}, + { + rect: {left: 0, top: 0, width: 100, height: 100}, + color: '#37afa9', + displayName: 'Group1Node1', + count: 1, + }, + ], + [ + {id: 2}, + { + rect: {left: 100, top: 0, width: 100, height: 100}, + color: '#37afa9', + displayName: 'Group2Node1', + count: 1, + }, + ], + ]); + + const result = groupAndSortNodes(nodeToData); + + expect(result).toEqual([ + [ + { + rect: {left: 0, top: 0, width: 100, height: 100}, + color: '#37afa9', + displayName: 'Group1Node1', + count: 1, + }, + ], + [ + { + rect: {left: 100, top: 0, width: 100, height: 100}, + color: '#37afa9', + displayName: 'Group2Node1', + count: 1, + }, + ], + ]); + }); + + test('should filter out nodes without rect property', () => { + const nodeToData = new Map([ + [ + {id: 1}, + { + rect: null, + color: '#37afa9', + displayName: 'NoRectNode', + count: 1, + }, + ], + [ + {id: 2}, + { + rect: undefined, + color: '#63b19e', + displayName: 'UndefinedRectNode', + count: 2, + }, + ], + [ + {id: 3}, + { + rect: {left: 0, top: 0, width: 100, height: 100}, + color: '#80b393', + displayName: 'ValidNode', + count: 3, + }, + ], + ]); + + const result = groupAndSortNodes(nodeToData); + + expect(result).toEqual([ + [ + { + rect: {left: 0, top: 0, width: 100, height: 100}, + color: '#80b393', + displayName: 'ValidNode', + count: 3, + }, + ], + ]); + }); +}); From 57900ddd42c05245d4d5eeb975f2a8b475433198 Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Wed, 11 Dec 2024 13:50:28 +0100 Subject: [PATCH 10/17] fix: remove console log --- .../src/devtools/views/Settings/SettingsContext.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js index 06d3651aacc..ec7b3ba9c9d 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js @@ -174,10 +174,6 @@ function SettingsContextController({ }, [bridge, traceUpdatesEnabled]); useEffect(() => { - console.log( - 'sent to bridge: setShowNamesWhenTracing', - showNamesWhenTracing, - ); bridge.send('setShowNamesWhenTracing', showNamesWhenTracing); }, [bridge, showNamesWhenTracing]); From 36b57985cfdd826cc32e42d21d5204bc88cb9850 Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Wed, 11 Dec 2024 13:50:28 +0100 Subject: [PATCH 11/17] fix(trace-updates): conditionally get component names based on setting --- .../src/backend/views/TraceUpdates/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js index dac5f76b558..93643074ca8 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js @@ -96,7 +96,7 @@ function traceUpdates(nodes: Set): void { rect = measureNode(node); } - let displayName = agent.getComponentNameForHostInstance(node); + let displayName = this.showNames && agent.getComponentNameForHostInstance(node); if (displayName != null && displayName.startsWith('Forget(')) { displayName = '✨' + displayName.slice(7, -1); } From 4516046df8439d08a4c0fd00f419fea0ce944706 Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Wed, 11 Dec 2024 13:50:28 +0100 Subject: [PATCH 12/17] refactor(trace-updates): replace spread operator with explicit object construction --- .../src/backend/views/TraceUpdates/canvas.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js index 55b92311868..447fd70404c 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js @@ -137,7 +137,15 @@ function iterateNodes( nodeToData.forEach((data, node) => { const colorIndex = Math.min(COLORS.length - 1, data.count - 1); const color = COLORS[colorIndex]; - execute({...data, color, node}); + execute({ + color, + node, + count: data.count, + displayName: data.displayName, + expirationTime: data.expirationTime, + lastMeasuredAt: data.lastMeasuredAt, + rect: data.rect, + }); }); } From 9e84c54dcd003f722058064b719809a4aad13e63 Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Wed, 11 Dec 2024 15:24:34 +0100 Subject: [PATCH 13/17] feat(trace-updates): add grouped trace updates with names for React Native - Introduced the `drawGroupedTraceUpdatesWithNames` event in `Agent` for emitting grouped trace updates. - This maintains backward compatibility while improving trace update organization for React Native. Note: This change only covers the JavaScript implementation; the native side still needs to be implemented. --- packages/react-devtools-shared/src/backend/agent.js | 2 ++ .../src/backend/views/TraceUpdates/canvas.js | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/packages/react-devtools-shared/src/backend/agent.js b/packages/react-devtools-shared/src/backend/agent.js index 63288984de1..111c458bdbd 100644 --- a/packages/react-devtools-shared/src/backend/agent.js +++ b/packages/react-devtools-shared/src/backend/agent.js @@ -28,6 +28,7 @@ import type { DevToolsHookSettings, } from './types'; import type {ComponentFilter} from 'react-devtools-shared/src/frontend/types'; +import type {GroupItem} from './views/TraceUpdates/canvas'; import {isReactNativeEnvironment} from './utils'; import { sessionStorageGetItem, @@ -142,6 +143,7 @@ export default class Agent extends EventEmitter<{ shutdown: [], traceUpdates: [Set], drawTraceUpdates: [Array], + drawGroupedTraceUpdatesWithNames: [Array>], disableTraceUpdates: [], getIfHasUnsupportedRendererVersion: [], updateHookSettings: [$ReadOnly], diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js index 447fd70404c..3e013975467 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/canvas.js @@ -37,6 +37,9 @@ function drawNative(nodeToData: Map, agent: Agent) { }); agent.emit('drawTraceUpdates', nodesToDraw); + + const mergedNodes = groupAndSortNodes(nodeToData); + agent.emit('drawGroupedTraceUpdatesWithNames', mergedNodes); } function drawWeb(nodeToData: Map) { @@ -71,6 +74,8 @@ type GroupItem = { count: number, }; +export type {GroupItem}; + export function groupAndSortNodes( nodeToData: Map, ): Array> { From 4ba16b03ca1b500f0e21eae717197d058fc89346 Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Thu, 12 Dec 2024 16:20:58 +0100 Subject: [PATCH 14/17] feat(trace-updates): extract HOC names and add markers to display names --- .../src/backend/views/TraceUpdates/index.js | 29 ++++++++++++++----- .../src/backend/views/utils.js | 22 ++++++++++++++ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js index 93643074ca8..7049dd70829 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js @@ -9,7 +9,7 @@ import Agent from 'react-devtools-shared/src/backend/agent'; import {destroy as destroyCanvas, draw} from './canvas'; -import {getNestedBoundingClientRect} from '../utils'; +import {extractHOCNames, getNestedBoundingClientRect} from '../utils'; import type {HostInstance} from '../../types'; import type {Rect} from '../utils'; @@ -24,6 +24,12 @@ const MAX_DISPLAY_DURATION = 3000; // How long should a rect be considered valid for? const REMEASUREMENT_AFTER_DURATION = 250; +// Markers for different types of HOCs +const HOC_MARKERS = new Map([ + ['Forget', '✨ń'], + ['Memo', '🧠'], +]); + // Some environments (e.g. React Native / Hermes) don't support the performance API yet. const getCurrentTime = // $FlowFixMe[method-unbinding] @@ -81,9 +87,7 @@ export function toggleEnabled(value: boolean): void { } function traceUpdates(nodes: Set): void { - if (!isEnabled) { - return; - } + if (!isEnabled) return; nodes.forEach(node => { const data = nodeToData.get(node); @@ -91,14 +95,25 @@ function traceUpdates(nodes: Set): void { let lastMeasuredAt = data != null ? data.lastMeasuredAt : 0; let rect = data != null ? data.rect : null; + if (rect === null || lastMeasuredAt + REMEASUREMENT_AFTER_DURATION < now) { lastMeasuredAt = now; rect = measureNode(node); } - let displayName = this.showNames && agent.getComponentNameForHostInstance(node); - if (displayName != null && displayName.startsWith('Forget(')) { - displayName = '✨' + displayName.slice(7, -1); + let displayName = showNames + ? agent.getComponentNameForHostInstance(node) + : null; + if (displayName) { + const {baseComponentName, hocNames} = extractHOCNames(displayName); + + const markers = hocNames.map(hoc => HOC_MARKERS.get(hoc) || '').join(''); + + const enhancedDisplayName = markers + ? `${markers}${baseComponentName}` + : baseComponentName; + + displayName = enhancedDisplayName; } nodeToData.set(node, { diff --git a/packages/react-devtools-shared/src/backend/views/utils.js b/packages/react-devtools-shared/src/backend/views/utils.js index 009bb60ffa5..d94864f85bc 100644 --- a/packages/react-devtools-shared/src/backend/views/utils.js +++ b/packages/react-devtools-shared/src/backend/views/utils.js @@ -138,3 +138,25 @@ export function getElementDimensions(domElement: HTMLElement): { paddingBottom: parseInt(calculatedStyle.paddingBottom, 10), }; } + +export function extractHOCNames(displayName: string) { + if (!displayName) return {baseComponentName: '', hocNames: []}; + + const hocRegex = /([A-Z][a-zA-Z0-9]*?)\((.*)\)/g; + const hocNames: string[] = []; + let baseComponentName = displayName; + let match; + + while ((match = hocRegex.exec(baseComponentName)) != null) { + if (Array.isArray(match)) { + const [, hocName, inner] = match; + hocNames.push(hocName); + baseComponentName = inner; + } + } + + return { + baseComponentName, + hocNames, + }; +} From 4633a85ca29d2de5362dc14eb9ece679edf407bd Mon Sep 17 00:00:00 2001 From: Piotr Tomczewski Date: Fri, 13 Dec 2024 08:54:30 +0100 Subject: [PATCH 15/17] fix: add missing return type --- packages/react-devtools-shared/src/backend/views/utils.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/backend/views/utils.js b/packages/react-devtools-shared/src/backend/views/utils.js index d94864f85bc..ea12105de96 100644 --- a/packages/react-devtools-shared/src/backend/views/utils.js +++ b/packages/react-devtools-shared/src/backend/views/utils.js @@ -139,7 +139,10 @@ export function getElementDimensions(domElement: HTMLElement): { }; } -export function extractHOCNames(displayName: string) { +export function extractHOCNames(displayName: string):{ + baseComponentName: string, + hocNames: string[], +} { if (!displayName) return {baseComponentName: '', hocNames: []}; const hocRegex = /([A-Z][a-zA-Z0-9]*?)\((.*)\)/g; From bea90c4095d50dd80eeff0f73b690218ce7443a3 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 13 Dec 2024 11:37:37 +0000 Subject: [PATCH 16/17] fix[utils]: lint --- packages/react-devtools-shared/src/backend/views/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/backend/views/utils.js b/packages/react-devtools-shared/src/backend/views/utils.js index ea12105de96..595b87c4818 100644 --- a/packages/react-devtools-shared/src/backend/views/utils.js +++ b/packages/react-devtools-shared/src/backend/views/utils.js @@ -139,7 +139,7 @@ export function getElementDimensions(domElement: HTMLElement): { }; } -export function extractHOCNames(displayName: string):{ +export function extractHOCNames(displayName: string): { baseComponentName: string, hocNames: string[], } { From ee3b328fc494dda9144a43a86824f0b91ea69427 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 13 Dec 2024 11:42:04 +0000 Subject: [PATCH 17/17] fix[TraceUpdates]: update Forget marker prefix --- .../src/backend/views/TraceUpdates/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js b/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js index 7049dd70829..48e504eddbb 100644 --- a/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js +++ b/packages/react-devtools-shared/src/backend/views/TraceUpdates/index.js @@ -26,7 +26,7 @@ const REMEASUREMENT_AFTER_DURATION = 250; // Markers for different types of HOCs const HOC_MARKERS = new Map([ - ['Forget', '✨ń'], + ['Forget', '✨'], ['Memo', '🧠'], ]);