[DRAFT] Import scheduling profiler into DevTools Profiler (#21897)
Brian Vaughn committed
Jul 22, 2021 at 13:58 UTC
f4161c3ec7d2ab2993695a458f771bb331e256d5
63 files changed
+931
-1128
.circleci/config.yml
-52
@@ -227,48 +227,6 @@ jobs:
227
- store_artifacts:
228
path: ./build/devtools.tgz
229
230
- build_devtools_scheduling_profiler:
231
- docker: *docker
232
- environment: *environment
233
- steps:
234
- - checkout
235
- - attach_workspace: *attach_workspace
236
- - run: yarn workspaces info | head -n -1 > workspace_info.txt
237
- - *restore_yarn_cache
238
- - *restore_node_modules
239
- - run:
240
- name: Install Packages
241
- command: yarn --frozen-lockfile --cache-folder ~/.cache/yarn
242
- - run:
243
- name: Build and archive
244
- command: |
245
- mkdir -p build/devtools
246
- cd packages/react-devtools-scheduling-profiler
247
- yarn build
248
- cd dist
249
- tar -zcvf ../../../build/devtools-scheduling-profiler.tgz .
250
- - store_artifacts:
251
- path: ./build/devtools-scheduling-profiler.tgz
252
- - persist_to_workspace:
253
- root: packages/react-devtools-scheduling-profiler
254
- paths:
255
- - dist
256
-
257
- deploy_devtools_scheduling_profiler:
258
- docker: *docker
259
- environment: *environment
260
- steps:
261
- - checkout
262
- - attach_workspace:
263
- at: packages/react-devtools-scheduling-profiler
264
- - run: yarn workspaces info | head -n -1 > workspace_info.txt
265
- - *restore_node_modules
266
- - run:
267
- name: Deploy
268
- command: |
269
- cd packages/react-devtools-scheduling-profiler
270
- yarn vercel deploy dist --prod --confirm --token $SCHEDULING_PROFILER_DEPLOY_VERCEL_TOKEN
271
-
230
yarn_lint_build:
231
docker: *docker
232
environment: *environment
@@ -408,16 +366,6 @@ workflows:
366
- build_devtools_and_process_artifacts:
367
requires:
368
- yarn_build
411
- - build_devtools_scheduling_profiler:
412
- requires:
413
- - yarn_build
414
- - deploy_devtools_scheduling_profiler:
415
- requires:
416
- - build_devtools_scheduling_profiler
417
- filters:
418
- branches:
419
- only:
420
- - main
369
370
# New workflow that will replace "stable" and "experimental"
371
build_and_test:
packages/react-devtools-core/webpack.standalone.js
+25
-8
@@ -18,6 +18,15 @@ const __DEV__ = NODE_ENV === 'development';
18
19
const DEVTOOLS_VERSION = getVersionString();
20
21
+const babelOptions = {
22
+ configFile: resolve(
23
+ __dirname,
24
+ '..',
25
+ 'react-devtools-shared',
26
+ 'babel.config.js',
27
+ ),
28
+};
29
+
30
module.exports = {
31
mode: __DEV__ ? 'development' : 'production',
32
devtool: __DEV__ ? 'cheap-module-eval-source-map' : 'source-map',
@@ -62,17 +71,25 @@ module.exports = {
71
],
72
module: {
73
rules: [
74
+ {
75
+ test: /\.worker\.js$/,
76
+ use: [
77
+ {
78
+ loader: 'workerize-loader',
79
+ options: {
80
+ inline: true,
81
+ },
82
+ },
83
+ {
84
+ loader: 'babel-loader',
85
+ options: babelOptions,
86
+ },
87
+ ],
88
+ },
89
{
90
test: /\.js$/,
91
loader: 'babel-loader',
68
- options: {
69
- configFile: resolve(
70
- __dirname,
71
- '..',
72
- 'react-devtools-shared',
73
- 'babel.config.js',
74
- ),
75
- },
92
+ options: babelOptions,
93
},
94
{
95
test: /\.css$/,
packages/react-devtools-extensions/package.json
+1
-1
@@ -37,6 +37,7 @@
37
"chrome-launch": "^1.1.4",
38
"crx": "^5.0.0",
39
"css-loader": "^1.0.1",
40
+ "file-loader": "^6.1.0",
41
"firefox-profile": "^1.0.2",
42
"fs-extra": "^4.0.2",
43
"jest-fetch-mock": "^3.0.3",
@@ -52,7 +53,6 @@
53
"source-map": "^0.8.0-beta.0",
54
"sourcemap-codec": "^1.4.8",
55
"style-loader": "^0.23.1",
55
- "web-ext": "^3.0.0",
56
"webpack": "^4.43.0",
57
"webpack-cli": "^3.3.11",
58
"webpack-dev-server": "^3.10.3",
packages/react-devtools-extensions/src/main.js
+2
@@ -140,6 +140,8 @@ function createPanelIfReactLoaded() {
140
isProfiling,
141
supportsReloadAndProfile: isChrome,
142
supportsProfiling,
143
+ // At this time, the scheduling profiler can only parse Chrome performance profiles.
144
+ supportsSchedulingProfiler: isChrome,
145
supportsTraceUpdates: true,
146
});
147
store.profilerStore.profilingData = profilingData;
packages/react-devtools-extensions/src/parseHookNames/index.js
+2
-3
@@ -9,9 +9,8 @@
9
* @flow
10
*/
11
12
-// This file uses workerize to load ./parseHookNames.worker as a webworker
13
-// and instanciates it, exposing flow typed functions that can be used
14
-// on other files.
12
+// This file uses workerize to load ./parseHookNames.worker as a webworker and instanciates it,
13
+// exposing flow typed functions that can be used on other files.
14
15
import * as parseHookNamesModule from './parseHookNames';
16
import WorkerizedParseHookNames from './parseHookNames.worker';
packages/react-devtools-extensions/src/parseHookNames/parseHookNames.worker.js
+7
@@ -1,3 +1,10 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
import * as parseHookNamesModule from './parseHookNames';
9
10
export const parseHookNames = parseHookNamesModule.parseHookNames;
packages/react-devtools-extensions/webpack.config.js
+25
-13
@@ -19,6 +19,15 @@ const DEVTOOLS_VERSION = getVersionString();
19
20
const featureFlagTarget = process.env.FEATURE_FLAG_TARGET || 'extension-oss';
21
22
+const babelOptions = {
23
+ configFile: resolve(
24
+ __dirname,
25
+ '..',
26
+ 'react-devtools-shared',
27
+ 'babel.config.js',
28
+ ),
29
+};
30
+
31
module.exports = {
32
mode: __DEV__ ? 'development' : 'production',
33
devtool: __DEV__ ? 'cheap-module-eval-source-map' : false,
@@ -81,17 +90,25 @@ module.exports = {
90
],
91
92
rules: [
93
+ {
94
+ test: /\.worker\.js$/,
95
+ use: [
96
+ {
97
+ loader: 'workerize-loader',
98
+ options: {
99
+ inline: true,
100
+ },
101
+ },
102
+ {
103
+ loader: 'babel-loader',
104
+ options: babelOptions,
105
+ },
106
+ ],
107
+ },
108
{
109
test: /\.js$/,
110
loader: 'babel-loader',
87
- options: {
88
- configFile: resolve(
89
- __dirname,
90
- '..',
91
- 'react-devtools-shared',
92
- 'babel.config.js',
93
- ),
94
- },
111
+ options: babelOptions,
112
},
113
{
114
test: /\.css$/,
@@ -109,11 +126,6 @@ module.exports = {
126
},
127
],
128
},
112
- {
113
- test: /\.worker\.js$/,
114
- // inline: true due to limitations with extensions
115
- use: {loader: 'workerize-loader', options: {inline: true}},
116
- },
129
],
130
},
131
};
packages/react-devtools-inline/package.json
+3
-1
@@ -34,10 +34,12 @@
34
"babel-loader": "^8.0.4",
35
"cross-env": "^3.1.4",
36
"css-loader": "^1.0.1",
37
+ "file-loader": "^6.1.0",
38
"raw-loader": "^3.1.0",
39
"style-loader": "^0.23.1",
40
"webpack": "^4.43.0",
41
"webpack-cli": "^3.3.11",
41
- "webpack-dev-server": "^3.10.3"
42
+ "webpack-dev-server": "^3.10.3",
43
+ "worker-loader": "^3.0.3"
44
}
45
}
packages/react-devtools-inline/src/frontend.js
+1
@@ -24,6 +24,7 @@ export function createStore(bridge: FrontendBridge): Store {
24
return new Store(bridge, {
25
checkBridgeProtocolCompatibility: true,
26
supportsTraceUpdates: true,
27
+ supportsSchedulingProfiler: true,
28
});
29
}
30
packages/react-devtools-inline/webpack.config.js
+25
-8
@@ -16,6 +16,15 @@ const __DEV__ = NODE_ENV === 'development';
16
17
const DEVTOOLS_VERSION = getVersionString();
18
19
+const babelOptions = {
20
+ configFile: resolve(
21
+ __dirname,
22
+ '..',
23
+ 'react-devtools-shared',
24
+ 'babel.config.js',
25
+ ),
26
+};
27
+
28
module.exports = {
29
mode: __DEV__ ? 'development' : 'production',
30
devtool: __DEV__ ? 'eval-cheap-source-map' : 'source-map',
@@ -65,17 +74,25 @@ module.exports = {
74
],
75
module: {
76
rules: [
77
+ {
78
+ test: /\.worker\.js$/,
79
+ use: [
80
+ {
81
+ loader: 'workerize-loader',
82
+ options: {
83
+ inline: true,
84
+ },
85
+ },
86
+ {
87
+ loader: 'babel-loader',
88
+ options: babelOptions,
89
+ },
90
+ ],
91
+ },
92
{
93
test: /\.js$/,
94
loader: 'babel-loader',
71
- options: {
72
- configFile: resolve(
73
- __dirname,
74
- '..',
75
- 'react-devtools-shared',
76
- 'babel.config.js',
77
- ),
78
- },
95
+ options: babelOptions,
96
},
97
{
98
test: /\.css$/,
packages/react-devtools-scheduling-profiler/README.md
+2
-14
@@ -1,15 +1,3 @@
1
-# Experimental React Concurrent Mode Profiler
1
+# React Concurrent Mode Profiler
2
3
-https://react-devtools-scheduling-profiler.vercel.app/
4
-
5
-## Setting up continuous deployment with CircleCI and Vercel
6
-
7
-These instructions are intended for internal use, but may be useful if you are setting up a custom production deployment of the scheduling profiler.
8
-
9
-1. Create a Vercel token at https://vercel.com/account/tokens.
10
-2. Configure CircleCI:
11
- 1. In CircleCI, navigate to the repository's Project Settings.
12
- 2. In the Advanced tab, ensure that "Pass secrets to builds from forked pull requests" is set to false.
13
- 3. In the Environment Variables tab, add the Vercel token as a new `SCHEDULING_PROFILER_DEPLOY_VERCEL_TOKEN` environment variable.
14
-
15
-The Vercel project will be created when the deploy job runs.
3
+This package contains the new/experimental "scheduling profiler" for React 18. This profiler exists as its own project because it was initially deployed as a standalone app. It has since been moved into the DevTools Profiler under the "Scheduling" tab. This package will likely eventually be moved into `react-devtools-shared`.
\ No newline at end of file
packages/react-devtools-scheduling-profiler/buildUtils.js
deleted
-36
@@ -1,36 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- */
7
-
8
-const {execSync} = require('child_process');
9
-const {readFileSync} = require('fs');
10
-const {resolve} = require('path');
11
-
12
-function getGitCommit() {
13
- try {
14
- return execSync('git show -s --format=%h')
15
- .toString()
16
- .trim();
17
- } catch (error) {
18
- // Mozilla runs this command from a git archive.
19
- // In that context, there is no Git revision.
20
- return null;
21
- }
22
-}
23
-
24
-function getVersionString() {
25
- const packageVersion = JSON.parse(
26
- readFileSync(resolve(__dirname, './package.json')),
27
- ).version;
28
-
29
- const commit = getGitCommit();
30
-
31
- return `${packageVersion}-${commit}`;
32
-}
33
-
34
-module.exports = {
35
- getVersionString,
36
-};
packages/react-devtools-scheduling-profiler/package.json
+1
-5
@@ -1,12 +1,8 @@
1
{
2
"private": true,
3
"name": "react-devtools-scheduling-profiler",
4
- "version": "0.0.0",
4
+ "version": "4.14.0",
5
"license": "MIT",
6
- "scripts": {
7
- "build": "cross-env NODE_ENV=production cross-env TARGET=remote webpack --config webpack.config.js",
8
- "start": "cross-env NODE_ENV=development cross-env TARGET=local webpack-dev-server --open"
9
- },
6
"dependencies": {
7
"@elg/speedscope": "1.9.0-a6f84db",
8
"clipboard-js": "^0.3.6",
packages/react-devtools-scheduling-profiler/src/App.css
deleted
-19
@@ -1,19 +0,0 @@
1
-.DevTools {
2
- width: 100%;
3
- height: 100%;
4
- display: flex;
5
- flex-direction: column;
6
- background-color: var(--color-background);
7
- color: var(--color-text);
8
-}
9
-
10
-.TabContent {
11
- flex: 1 1 100%;
12
- overflow: auto;
13
- -webkit-app-region: no-drag;
14
-}
15
-
16
-.DevTools, .DevTools * {
17
- box-sizing: border-box;
18
- -webkit-font-smoothing: var(--font-smoothing);
19
-}
packages/react-devtools-scheduling-profiler/src/App.js
deleted
-34
@@ -1,34 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-// Reach styles need to come before any component styles.
11
-// This makes overriding the styles simpler.
12
-import '@reach/menu-button/styles.css';
13
-import '@reach/tooltip/styles.css';
14
-
15
-import * as React from 'react';
16
-
17
-import {SchedulingProfiler} from './SchedulingProfiler';
18
-import {useBrowserTheme, useDisplayDensity} from './hooks';
19
-
20
-import styles from './App.css';
21
-import 'react-devtools-shared/src/devtools/views/root.css';
22
-
23
-export default function App() {
24
- useBrowserTheme();
25
- useDisplayDensity();
26
-
27
- return (
28
- <div className={styles.DevTools}>
29
- <div className={styles.TabContent}>
30
- <SchedulingProfiler />
31
- </div>
32
- </div>
33
- );
34
-}
packages/react-devtools-scheduling-profiler/src/CanvasPage.css
+4
-4
@@ -1,7 +1,7 @@
1
.CanvasPage {
2
position: absolute;
3
- top: 0.5rem;
4
- bottom: 0.5rem;
5
- left: 0.5rem;
6
- right: 0.5rem;
3
+ top: 0;
4
+ bottom: 0;
5
+ left: 0;
6
+ right: 0;
7
}
packages/react-devtools-scheduling-profiler/src/CanvasPage.js
+11
-6
@@ -52,9 +52,9 @@ import {
52
import {COLORS} from './content-views/constants';
53
54
import EventTooltip from './EventTooltip';
55
-import ContextMenu from './context/ContextMenu';
56
-import ContextMenuItem from './context/ContextMenuItem';
57
-import useContextMenu from './context/useContextMenu';
55
+import ContextMenu from 'react-devtools-shared/src/devtools/ContextMenu/ContextMenu';
56
+import ContextMenuItem from 'react-devtools-shared/src/devtools/ContextMenu/ContextMenuItem';
57
+import useContextMenu from 'react-devtools-shared/src/devtools/ContextMenu/useContextMenu';
58
import {getBatchRange} from './utils/getBatchRange';
59
60
import styles from './CanvasPage.css';
@@ -94,6 +94,7 @@ const copySummary = (data: ReactProfilerData, measure: ReactMeasure) => {
94
);
95
};
96
97
+// TODO (scheduling profiler) Why is the "zoom" feature so much slower than normal rendering?
98
const zoomToBatch = (
99
data: ReactProfilerData,
100
measure: ReactMeasure,
@@ -102,8 +103,7 @@ const zoomToBatch = (
103
const {batchUID} = measure;
104
const [startTime, stopTime] = getBatchRange(batchUID, data);
105
syncedHorizontalPanAndZoomViews.forEach(syncedView =>
105
- // Using time as range works because the views' intrinsic content size is
106
- // based on time.
106
+ // Using time as range works because the views' intrinsic content size is based on time.
107
syncedView.zoomToRange(startTime, stopTime),
108
);
109
};
@@ -243,6 +243,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
243
defaultFrame,
244
reactMeasuresHorizontalPanAndZoomView,
245
flamechartHorizontalPanAndZoomView,
246
+ canvasRef,
247
);
248
249
const rootView = new View(
@@ -281,13 +282,17 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
282
283
useCanvasInteraction(canvasRef, interactor);
284
285
+ const setIsContextMenuShownWrapper = (...args) => {
286
+ console.log('setIsContextMenuShown()', ...args);
287
+ setIsContextMenuShown(...args);
288
+ };
289
useContextMenu({
290
data: {
291
data,
292
hoveredEvent,
293
},
294
id: CONTEXT_MENU_ID,
290
- onChange: setIsContextMenuShown,
295
+ onChange: setIsContextMenuShownWrapper,
296
ref: canvasRef,
297
});
298
packages/react-devtools-scheduling-profiler/src/EventTooltip.css
+9
-8
@@ -6,9 +6,10 @@
6
padding: 0.25rem;
7
user-select: none;
8
pointer-events: none;
9
- background-color: #ffffff;
10
- border: 1px solid #ccc;
11
- box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
9
+ background-color: var(--color-tooltip-background);
10
+ border: 1px solid var(border);
11
+ box-shadow: 1px 1px 2px var(--color-shadow);
12
+ color: var(--color-tooltip-text);
13
font-size: 11px;
14
}
15
@@ -26,7 +27,7 @@
27
}
28
29
.DetailsGridLabel {
29
- color: #666;
30
+ color: var(--color-dim);
31
text-align: right;
32
}
33
@@ -56,14 +57,14 @@
57
line-height: 1.5;
58
-webkit-mask-image: linear-gradient(
59
180deg,
59
- #fff,
60
- #fff 5em,
60
+ var(--color-tooltip-background),
61
+ var(--color-tooltip-background) 5em,
62
transparent
63
);
64
mask-image: linear-gradient(
65
180deg,
65
- #fff,
66
- #fff 5em,
66
+ var(--color-tooltip-background),
67
+ var(--color-tooltip-background) 5em,
68
transparent
69
);
70
white-space: pre;
packages/react-devtools-scheduling-profiler/src/ImportButton.js
+3
-2
@@ -9,17 +9,17 @@
9
10
import * as React from 'react';
11
import {useCallback, useRef} from 'react';
12
-
12
import Button from 'react-devtools-shared/src/devtools/views/Button';
13
import ButtonIcon from 'react-devtools-shared/src/devtools/views/ButtonIcon';
14
15
import styles from './ImportButton.css';
16
17
type Props = {|
18
+ children?: mixed,
19
onFileSelect: (file: File) => void,
20
|};
21
22
-export default function ImportButton({onFileSelect}: Props) {
22
+export default function ImportButton({children = null, onFileSelect}: Props) {
23
const inputRef = useRef<HTMLInputElement | null>(null);
24
25
const handleFiles = useCallback(() => {
@@ -51,6 +51,7 @@ export default function ImportButton({onFileSelect}: Props) {
51
/>
52
<Button onClick={uploadData} title="Load profile...">
53
<ButtonIcon type="import" />
54
+ {children}
55
</Button>
56
</>
57
);
packages/react-devtools-scheduling-profiler/src/SchedulingProfiler.css
+14
-61
@@ -1,21 +1,5 @@
1
-.SchedulingProfiler {
2
- width: 100%;
3
- height: 100%;
4
- position: relative;
5
- display: flex;
6
- flex-direction: column;
7
- font-family: var(--font-family-sans);
8
- font-size: var(--font-size-sans-normal);
9
- background-color: var(--color-background);
10
- color: var(--color-text);
11
-}
12
-
13
-.SchedulingProfiler, .SchedulingProfiler * {
14
- box-sizing: border-box;
15
- -webkit-font-smoothing: var(--font-smoothing);
16
-}
17
-
1
.Content {
2
+ width: 100%;
3
position: relative;
4
flex: 1 1 auto;
5
display: flex;
@@ -52,57 +36,26 @@
36
margin-bottom: 0.5rem;
37
}
38
55
-.Toolbar {
56
- height: 2.25rem;
57
- padding: 0 0.25rem;
58
- flex: 0 0 auto;
59
- display: flex;
60
- align-items: center;
61
- border-bottom: 1px solid var(--color-border);
62
-}
63
-
64
-.VRule {
65
- height: 20px;
66
- width: 1px;
67
- border-left: 1px solid var(--color-border);
68
- padding-left: 0.25rem;
69
- margin-left: 0.25rem;
70
-}
71
-
72
-.Spacer {
73
- flex: 1;
74
-}
75
-
76
-.Link {
77
- color: var(--color-button);
78
-}
79
-
80
-.ScreenshotWrapper {
81
- max-width: 30rem;
82
- padding: 0 1rem;
83
- margin-bottom: 2rem;
39
+.WelcomeInstructionsList {
40
}
41
86
-.Screenshot {
87
- width: 100%;
88
- border-radius: 0.4em;
89
- border: 2px solid var(--color-border);
42
+.WelcomeInstructionsListItem {
43
+ display: flex;
44
+ align-items: center;
45
+ line-height: 1.5rem;
46
+ counter-increment: li;
47
}
48
92
-.AppName {
93
- font-size: var(--font-size-sans-large);
49
+.WelcomeInstructionsListItem::before {
50
+ content: counter(li);
51
margin-right: 0.5rem;
95
- user-select: none;
52
}
53
98
-@media screen and (max-width: 350px) {
99
- .AppName {
100
- display: none;
101
- }
54
+.WelcomeInstructionsListItemLink {
55
+ color: var(--color-link);
56
+ margin-left: 0.25rem;
57
}
58
104
-@media screen and (max-height: 600px) {
105
- .ScreenshotWrapper {
106
- display: none;
107
- }
59
+.ImportButtonLabel {
60
+ margin-left: 0.25rem;
61
}
\ No newline at end of file
packages/react-devtools-scheduling-profiler/src/SchedulingProfiler.js
+66
-80
@@ -7,102 +7,88 @@
7
* @flow
8
*/
9
10
-import type {Resource} from 'react-devtools-shared/src/devtools/cache';
11
-import type {ReactProfilerData} from './types';
12
-import type {ImportWorkerOutputData} from './import-worker/import.worker';
10
+import type {DataResource} from './createDataResourceFromImportedFile';
11
12
import * as React from 'react';
15
-import {Suspense, useCallback, useState} from 'react';
16
-import {createResource} from 'react-devtools-shared/src/devtools/cache';
17
-import ReactLogo from 'react-devtools-shared/src/devtools/views/ReactLogo';
18
-
13
+import {
14
+ Suspense,
15
+ useContext,
16
+ useDeferredValue,
17
+ useLayoutEffect,
18
+ useState,
19
+} from 'react';
20
+import {SettingsContext} from 'react-devtools-shared/src/devtools/views/Settings/SettingsContext';
21
+import {updateColorsToMatchTheme} from './content-views/constants';
22
+import {SchedulingProfilerContext} from './SchedulingProfilerContext';
23
import ImportButton from './ImportButton';
24
import CanvasPage from './CanvasPage';
21
-import ImportWorker from './import-worker/import.worker';
25
23
-import profilerBrowser from './assets/profilerBrowser.png';
26
import styles from './SchedulingProfiler.css';
27
26
-type DataResource = Resource<void, File, ReactProfilerData | Error>;
27
-
28
-function createDataResourceFromImportedFile(file: File): DataResource {
29
- return createResource(
30
- () => {
31
- return new Promise<ReactProfilerData | Error>((resolve, reject) => {
32
- const worker: Worker = new (ImportWorker: any)();
33
-
34
- worker.onmessage = function(event) {
35
- const data = ((event.data: any): ImportWorkerOutputData);
36
- switch (data.status) {
37
- case 'SUCCESS':
38
- resolve(data.processedData);
39
- break;
40
- case 'INVALID_PROFILE_ERROR':
41
- resolve(data.error);
42
- break;
43
- case 'UNEXPECTED_ERROR':
44
- reject(data.error);
45
- break;
46
- }
47
- worker.terminate();
48
- };
49
-
50
- worker.postMessage({file});
51
- });
52
- },
53
- () => file,
54
- {useWeakMap: true},
55
- );
56
-}
57
-
28
export function SchedulingProfiler(_: {||}) {
59
- const [dataResource, setDataResource] = useState<DataResource | null>(null);
29
+ const {importSchedulingProfilerData, schedulingProfilerData} = useContext(
30
+ SchedulingProfilerContext,
31
+ );
32
61
- const handleFileSelect = useCallback((file: File) => {
62
- setDataResource(createDataResourceFromImportedFile(file));
63
- }, []);
33
+ // HACK: Canvas rendering uses an imperative API,
34
+ // but DevTools colors are stored in CSS variables (see root.css and SettingsContext).
35
+ // When the theme changes, we need to trigger update the imperative colors and re-draw the Canvas.
36
+ const {theme} = useContext(SettingsContext);
37
+ // HACK: SettingsContext also uses a useLayoutEffect to update styles;
38
+ // make sure the theme context in SettingsContext updates before this code.
39
+ const deferredTheme = useDeferredValue(theme);
40
+ // HACK: Schedule a re-render of the Canvas once colors have been updated.
41
+ // The easiest way to guarangee this happens is to recreate the inner Canvas component.
42
+ const [key, setKey] = useState<string>(theme);
43
+ useLayoutEffect(() => {
44
+ updateColorsToMatchTheme();
45
+ setKey(deferredTheme);
46
+ }, [deferredTheme]);
47
48
return (
66
- <div className={styles.SchedulingProfiler}>
67
- <div className={styles.Toolbar}>
68
- <ReactLogo />
69
- <span className={styles.AppName}>Concurrent Mode Profiler</span>
70
- <div className={styles.VRule} />
71
- <ImportButton onFileSelect={handleFileSelect} />
72
- <div className={styles.Spacer} />
73
- </div>
74
- <div className={styles.Content}>
75
- {dataResource ? (
76
- <Suspense fallback={<ProcessingData />}>
77
- <DataResourceComponent
78
- dataResource={dataResource}
79
- onFileSelect={handleFileSelect}
80
- />
81
- </Suspense>
82
- ) : (
83
- <Welcome onFileSelect={handleFileSelect} />
84
- )}
85
- </div>
49
+ <div className={styles.Content}>
50
+ {schedulingProfilerData ? (
51
+ <Suspense fallback={<ProcessingData />}>
52
+ <DataResourceComponent
53
+ dataResource={schedulingProfilerData}
54
+ key={key}
55
+ onFileSelect={importSchedulingProfilerData}
56
+ />
57
+ </Suspense>
58
+ ) : (
59
+ <Welcome onFileSelect={importSchedulingProfilerData} />
60
+ )}
61
</div>
62
);
63
}
64
65
const Welcome = ({onFileSelect}: {|onFileSelect: (file: File) => void|}) => (
91
- <div className={styles.EmptyStateContainer}>
92
- <div className={styles.ScreenshotWrapper}>
93
- <img
94
- src={profilerBrowser}
95
- className={styles.Screenshot}
96
- alt="Profiler screenshot"
97
- />
98
- </div>
99
- <div className={styles.Header}>Welcome!</div>
100
- <div className={styles.Row}>
101
- Click the import button
102
- <ImportButton onFileSelect={onFileSelect} /> to import a Chrome
103
- performance profile.
104
- </div>
105
- </div>
66
+ <ol className={styles.WelcomeInstructionsList}>
67
+ <li className={styles.WelcomeInstructionsListItem}>
68
+ Open a website that's built with the
69
+ <a
70
+ className={styles.WelcomeInstructionsListItemLink}
71
+ href="https://reactjs.org/link/profiling"
72
+ rel="noopener noreferrer"
73
+ target="_blank">
74
+ profiling build of ReactDOM
75
+ </a>
76
+ .
77
+ </li>
78
+ <li className={styles.WelcomeInstructionsListItem}>
79
+ Open the "Performance" tab in Chrome and record some performance data.
80
+ </li>
81
+ <li className={styles.WelcomeInstructionsListItem}>
82
+ Click the "Save profile..." button in Chrome to export the data.
83
+ </li>
84
+ <li className={styles.WelcomeInstructionsListItem}>
85
+ Import the data into the profiler:
86
+ <br />
87
+ <ImportButton onFileSelect={onFileSelect}>
88
+ <span className={styles.ImportButtonLabel}>Import</span>
89
+ </ImportButton>
90
+ </li>
91
+ </ol>
92
);
93
94
const ProcessingData = () => (
packages/react-devtools-scheduling-profiler/src/SchedulingProfilerContext.js
new
+62
@@ -0,0 +1,62 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+import * as React from 'react';
11
+import {createContext, useCallback, useMemo, useState} from 'react';
12
+import createDataResourceFromImportedFile from './createDataResourceFromImportedFile';
13
+
14
+import type {DataResource} from './createDataResourceFromImportedFile';
15
+
16
+export type Context = {|
17
+ importSchedulingProfilerData: (file: File) => void,
18
+ schedulingProfilerData: DataResource | null,
19
+|};
20
+
21
+const SchedulingProfilerContext = createContext<Context>(
22
+ ((null: any): Context),
23
+);
24
+SchedulingProfilerContext.displayName = 'SchedulingProfilerContext';
25
+
26
+type Props = {|
27
+ children: React$Node,
28
+|};
29
+
30
+function SchedulingProfilerContextController({children}: Props) {
31
+ const [
32
+ schedulingProfilerData,
33
+ setSchedulingProfilerData,
34
+ ] = useState<DataResource | null>(null);
35
+
36
+ const importSchedulingProfilerData = useCallback((file: File) => {
37
+ setSchedulingProfilerData(createDataResourceFromImportedFile(file));
38
+ }, []);
39
+
40
+ // TODO (scheduling profiler) Start/stop time ref here?
41
+
42
+ const value = useMemo(
43
+ () => ({
44
+ importSchedulingProfilerData,
45
+ schedulingProfilerData,
46
+ // TODO (scheduling profiler)
47
+ }),
48
+ [
49
+ importSchedulingProfilerData,
50
+ schedulingProfilerData,
51
+ // TODO (scheduling profiler)
52
+ ],
53
+ );
54
+
55
+ return (
56
+ <SchedulingProfilerContext.Provider value={value}>
57
+ {children}
58
+ </SchedulingProfilerContext.Provider>
59
+ );
60
+}
61
+
62
+export {SchedulingProfilerContext, SchedulingProfilerContextController};
packages/react-devtools-scheduling-profiler/src/assets/logo.svg
deleted
-1
@@ -1 +0,0 @@
1
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 170.96 159.93"><defs><style>.cls-1{fill:#09d3ac;}</style></defs><path class="cls-1" d="M141.35,73.27c0-6.89-8.11-13-20.58-16.73,3-12.67,1.69-22.75-4.28-26.16a9.55,9.55,0,0,0-4.79-1.2c-5.57,0-12.61,3.89-19.72,10.62-7.11-6.68-14.13-10.55-19.69-10.55a9.46,9.46,0,0,0-4.86,1.22c-5.94,3.42-7.17,13.44-4.21,26.05-12.41,3.71-20.48,9.77-20.5,16.63s8.12,13,20.59,16.72c-3,12.68-1.7,22.75,4.28,26.16a9.41,9.41,0,0,0,4.78,1.2c5.58,0,12.62-3.89,19.73-10.62,7.1,6.68,14.12,10.55,19.69,10.55a9.59,9.59,0,0,0,4.86-1.22c5.94-3.42,7.16-13.44,4.21-26C133.27,86.18,141.34,80.12,141.35,73.27ZM96.56,42.06c8.19-7.33,13.31-8.12,15.13-8.12h0a4.71,4.71,0,0,1,2.42.58c2.86,1.63,4,7.38,3.09,15a56.73,56.73,0,0,1-1,5.79,97.7,97.7,0,0,0-12.58-2.07,98.47,98.47,0,0,0-8.24-10.08C95.75,42.79,96.15,42.42,96.56,42.06ZM71.77,78.94c.78,1.5,1.61,3,2.47,4.51S76,86.49,77,88c-2.71-.39-5.34-.88-7.84-1.46C69.86,84,70.75,81.5,71.77,78.94ZM69,59.9c2.53-.59,5.18-1.08,7.93-1.46-1,1.48-1.86,3-2.76,4.59s-1.69,3-2.46,4.52Q70.18,63.65,69,59.9Zm5.21,13.34q1.86-3.93,4.09-7.86c1.5-2.62,3.11-5.17,4.77-7.61,2.91-.22,5.91-.34,9-.33s6,.12,8.89.35c1.66,2.42,3.25,5,4.75,7.55s2.88,5.22,4.12,7.83c-1.23,2.62-2.6,5.25-4.08,7.85s-3.11,5.18-4.77,7.62c-2.91.23-5.91.34-9,.34s-6-.13-8.89-.36c-1.66-2.42-3.26-4.94-4.76-7.55S75.5,75.85,74.25,73.24Zm32.86-14.77c2.72.38,5.35.87,7.84,1.45-.74,2.47-1.62,5-2.64,7.55-.79-1.5-1.61-3-2.48-4.51S108,59.93,107.11,58.47Zm2.76,24.92q1.29-2.27,2.46-4.53c1,2.6,2,5.16,2.7,7.66-2.52.59-5.17,1.07-7.92,1.45Q108.52,85.75,109.87,83.39ZM92,46.56c1.8,1.92,3.57,4,5.3,6.23-1.71-.07-3.46-.12-5.23-.12s-3.58,0-5.33.12C88.45,50.57,90.2,48.48,92,46.56Zm-22.18-12A4.82,4.82,0,0,1,72.29,34a13.11,13.11,0,0,1,5.19,1.31,39.07,39.07,0,0,1,10,6.78l1.17,1.07a98.49,98.49,0,0,0-8.16,10,98.88,98.88,0,0,0-12.65,2.06c-.44-1.94-.8-3.84-1-5.67C65.8,42,67,36.24,69.81,34.6ZM64.53,85.26a58.75,58.75,0,0,1-5.54-2c-7.1-3-11.5-6.85-11.5-10.14S51.91,66,59,63.05a57.63,57.63,0,0,1,5.44-1.94A97.93,97.93,0,0,0,69,73.25,98.72,98.72,0,0,0,64.53,85.26Zm23,19.1c-8.19,7.33-13.31,8.11-15.14,8.11a4.69,4.69,0,0,1-2.42-.58c-2.86-1.63-4-7.38-3.09-15a56.07,56.07,0,0,1,1-5.78,99.51,99.51,0,0,0,12.58,2.06,97.17,97.17,0,0,0,8.24,10.08Zm4.57-4.51c-1.8-1.92-3.57-4-5.31-6.23,1.72.08,3.47.12,5.24.12s3.58,0,5.33-.11C95.63,95.85,93.87,97.93,92.09,99.85Zm22.18,12a4.82,4.82,0,0,1-2.48.59c-1.82,0-7-.8-15.16-8.1l-1.17-1.07a98.44,98.44,0,0,0,8.15-10,97,97,0,0,0,12.66-2.06c.44,1.94.79,3.84,1,5.67C118.27,104.42,117.12,110.18,114.27,111.81Zm10.8-28.44c-1.71.7-3.52,1.35-5.44,1.93a98.54,98.54,0,0,0-4.57-12.14,98.1,98.1,0,0,0,4.49-12,58.75,58.75,0,0,1,5.54,2c7.09,3,11.5,6.85,11.49,10.14S132.17,80.42,125.07,83.37ZM92,82.39a9.18,9.18,0,1,0-9.17-9.19A9.17,9.17,0,0,0,92,82.39ZM31,17.88V128.53H153.07V17.88ZM148.3,123.77H35.78V22.65H148.3Zm-85-33.9c-3,12.68-1.7,22.75,4.28,26.16a9.41,9.41,0,0,0,4.78,1.2c5.58,0,12.62-3.89,19.73-10.62,7.1,6.68,14.12,10.55,19.69,10.55a9.59,9.59,0,0,0,4.86-1.22c5.94-3.42,7.16-13.44,4.21-26,12.41-3.72,20.48-9.78,20.49-16.63s-8.11-13-20.58-16.73c3-12.67,1.69-22.75-4.28-26.16a9.55,9.55,0,0,0-4.79-1.2c-5.57,0-12.61,3.89-19.72,10.62-7.11-6.68-14.13-10.55-19.69-10.55a9.46,9.46,0,0,0-4.86,1.22c-5.94,3.42-7.17,13.44-4.21,26.05-12.41,3.71-20.48,9.77-20.5,16.63S50.84,86.13,63.31,89.87Zm24.21,14.49c-8.19,7.33-13.31,8.11-15.14,8.11a4.69,4.69,0,0,1-2.42-.58c-2.86-1.63-4-7.38-3.09-15a56.07,56.07,0,0,1,1-5.78,99.51,99.51,0,0,0,12.58,2.06,97.17,97.17,0,0,0,8.24,10.08Zm24.79-36.89c-.79-1.5-1.61-3-2.48-4.51s-1.8-3-2.72-4.49c2.72.38,5.35.87,7.84,1.45C114.21,62.39,113.33,64.92,112.31,67.47Zm2.72,19c-2.52.59-5.17,1.07-7.92,1.45q1.41-2.22,2.76-4.58t2.46-4.53C113.37,81.46,114.28,84,115,86.52Zm-5.21-13.35c-1.23,2.62-2.6,5.25-4.08,7.85s-3.11,5.18-4.77,7.62c-2.91.23-5.91.34-9,.34s-6-.13-8.89-.36c-1.66-2.42-3.26-4.94-4.76-7.55s-2.87-5.22-4.12-7.83q1.86-3.93,4.09-7.86c1.5-2.62,3.11-5.17,4.77-7.61,2.91-.22,5.91-.34,9-.33s6,.12,8.89.35c1.66,2.42,3.25,5,4.75,7.55S108.58,70.56,109.82,73.17ZM77,88c-2.71-.39-5.34-.88-7.84-1.46.74-2.46,1.63-5,2.65-7.55.78,1.5,1.61,3,2.47,4.51S76,86.49,77,88ZM74.21,63c-.87,1.5-1.69,3-2.46,4.52Q70.18,63.65,69,59.9c2.53-.59,5.18-1.08,7.93-1.46C76,59.92,75.11,61.45,74.21,63ZM92.09,99.85c-1.8-1.92-3.57-4-5.31-6.23,1.72.08,3.47.12,5.24.12s3.58,0,5.33-.11C95.63,95.85,93.87,97.93,92.09,99.85Zm22.18,12a4.82,4.82,0,0,1-2.48.59c-1.82,0-7-.8-15.16-8.1l-1.17-1.07a98.44,98.44,0,0,0,8.15-10,97,97,0,0,0,12.66-2.06c.44,1.94.79,3.84,1,5.67C118.27,104.42,117.12,110.18,114.27,111.81Zm5.28-50.66a58.75,58.75,0,0,1,5.54,2c7.09,3,11.5,6.85,11.49,10.14s-4.41,7.16-11.51,10.11c-1.71.7-3.52,1.35-5.44,1.93a98.54,98.54,0,0,0-4.57-12.14A98.1,98.1,0,0,0,119.55,61.15Zm-23-19.09c8.19-7.33,13.31-8.12,15.13-8.12h0a4.71,4.71,0,0,1,2.42.58c2.86,1.63,4,7.38,3.09,15a56.73,56.73,0,0,1-1,5.79,97.7,97.7,0,0,0-12.58-2.07,98.47,98.47,0,0,0-8.24-10.08C95.75,42.79,96.15,42.42,96.56,42.06ZM92,46.56c1.8,1.92,3.57,4,5.3,6.23-1.71-.07-3.46-.12-5.23-.12s-3.58,0-5.33.12C88.45,50.57,90.2,48.48,92,46.56Zm-22.18-12A4.82,4.82,0,0,1,72.29,34a13.11,13.11,0,0,1,5.19,1.31,39.07,39.07,0,0,1,10,6.78l1.17,1.07a98.49,98.49,0,0,0-8.16,10,98.88,98.88,0,0,0-12.65,2.06c-.44-1.94-.8-3.84-1-5.67C65.8,42,67,36.24,69.81,34.6ZM59,63.05a57.63,57.63,0,0,1,5.44-1.94A97.93,97.93,0,0,0,69,73.25a98.72,98.72,0,0,0-4.49,12,58.75,58.75,0,0,1-5.54-2c-7.1-3-11.5-6.85-11.5-10.14S51.91,66,59,63.05Zm33,1a9.18,9.18,0,1,0,9.17,9.19A9.17,9.17,0,0,0,92,64Zm0,0a9.18,9.18,0,1,0,9.17,9.19A9.17,9.17,0,0,0,92,64Zm0,0a9.18,9.18,0,1,0,9.17,9.19A9.17,9.17,0,0,0,92,64Zm49.35,9.24c0-6.89-8.11-13-20.58-16.73,3-12.67,1.69-22.75-4.28-26.16a9.55,9.55,0,0,0-4.79-1.2c-5.57,0-12.61,3.89-19.72,10.62-7.11-6.68-14.13-10.55-19.69-10.55a9.46,9.46,0,0,0-4.86,1.22c-5.94,3.42-7.17,13.44-4.21,26.05-12.41,3.71-20.48,9.77-20.5,16.63s8.12,13,20.59,16.72c-3,12.68-1.7,22.75,4.28,26.16a9.41,9.41,0,0,0,4.78,1.2c5.58,0,12.62-3.89,19.73-10.62,7.1,6.68,14.12,10.55,19.69,10.55a9.59,9.59,0,0,0,4.86-1.22c5.94-3.42,7.16-13.44,4.21-26C133.27,86.18,141.34,80.12,141.35,73.27ZM96.56,42.06c8.19-7.33,13.31-8.12,15.13-8.12h0a4.71,4.71,0,0,1,2.42.58c2.86,1.63,4,7.38,3.09,15a56.73,56.73,0,0,1-1,5.79,97.7,97.7,0,0,0-12.58-2.07,98.47,98.47,0,0,0-8.24-10.08C95.75,42.79,96.15,42.42,96.56,42.06ZM71.77,78.94c.78,1.5,1.61,3,2.47,4.51S76,86.49,77,88c-2.71-.39-5.34-.88-7.84-1.46C69.86,84,70.75,81.5,71.77,78.94ZM69,59.9c2.53-.59,5.18-1.08,7.93-1.46-1,1.48-1.86,3-2.76,4.59s-1.69,3-2.46,4.52Q70.18,63.65,69,59.9Zm5.21,13.34q1.86-3.93,4.09-7.86c1.5-2.62,3.11-5.17,4.77-7.61,2.91-.22,5.91-.34,9-.33s6,.12,8.89.35c1.66,2.42,3.25,5,4.75,7.55s2.88,5.22,4.12,7.83c-1.23,2.62-2.6,5.25-4.08,7.85s-3.11,5.18-4.77,7.62c-2.91.23-5.91.34-9,.34s-6-.13-8.89-.36c-1.66-2.42-3.26-4.94-4.76-7.55S75.5,75.85,74.25,73.24Zm32.86-14.77c2.72.38,5.35.87,7.84,1.45-.74,2.47-1.62,5-2.64,7.55-.79-1.5-1.61-3-2.48-4.51S108,59.93,107.11,58.47Zm2.76,24.92q1.29-2.27,2.46-4.53c1,2.6,2,5.16,2.7,7.66-2.52.59-5.17,1.07-7.92,1.45Q108.52,85.75,109.87,83.39ZM92,46.56c1.8,1.92,3.57,4,5.3,6.23-1.71-.07-3.46-.12-5.23-.12s-3.58,0-5.33.12C88.45,50.57,90.2,48.48,92,46.56Zm-22.18-12A4.82,4.82,0,0,1,72.29,34a13.11,13.11,0,0,1,5.19,1.31,39.07,39.07,0,0,1,10,6.78l1.17,1.07a98.49,98.49,0,0,0-8.16,10,98.88,98.88,0,0,0-12.65,2.06c-.44-1.94-.8-3.84-1-5.67C65.8,42,67,36.24,69.81,34.6ZM64.53,85.26a58.75,58.75,0,0,1-5.54-2c-7.1-3-11.5-6.85-11.5-10.14S51.91,66,59,63.05a57.63,57.63,0,0,1,5.44-1.94A97.93,97.93,0,0,0,69,73.25,98.72,98.72,0,0,0,64.53,85.26Zm23,19.1c-8.19,7.33-13.31,8.11-15.14,8.11a4.69,4.69,0,0,1-2.42-.58c-2.86-1.63-4-7.38-3.09-15a56.07,56.07,0,0,1,1-5.78,99.51,99.51,0,0,0,12.58,2.06,97.17,97.17,0,0,0,8.24,10.08Zm4.57-4.51c-1.8-1.92-3.57-4-5.31-6.23,1.72.08,3.47.12,5.24.12s3.58,0,5.33-.11C95.63,95.85,93.87,97.93,92.09,99.85Zm22.18,12a4.82,4.82,0,0,1-2.48.59c-1.82,0-7-.8-15.16-8.1l-1.17-1.07a98.44,98.44,0,0,0,8.15-10,97,97,0,0,0,12.66-2.06c.44,1.94.79,3.84,1,5.67C118.27,104.42,117.12,110.18,114.27,111.81Zm10.8-28.44c-1.71.7-3.52,1.35-5.44,1.93a98.54,98.54,0,0,0-4.57-12.14,98.1,98.1,0,0,0,4.49-12,58.75,58.75,0,0,1,5.54,2c7.09,3,11.5,6.85,11.49,10.14S132.17,80.42,125.07,83.37ZM92,82.39a9.18,9.18,0,1,0-9.17-9.19A9.17,9.17,0,0,0,92,82.39Zm-69.32,54.5V26.2L17.89,31V141.66H139.94l4.78-4.77Z"/></svg>
\ No newline at end of file
packages/react-devtools-scheduling-profiler/src/assets/profilerBrowser.png
Binary files a/packages/react-devtools-scheduling-profiler/src/assets/profilerBrowser.png and /dev/null differ
packages/react-devtools-scheduling-profiler/src/assets/reactlogo.svg
deleted
-7
@@ -1,7 +0,0 @@
1
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
2
- <g fill="#61DAFB">
3
- <path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
4
- <circle cx="420.9" cy="296.5" r="45.7"/>
5
- <path d="M520.5 78.1z"/>
6
- </g>
7
-</svg>
packages/react-devtools-scheduling-profiler/src/content-views/FlamechartView.js
+1
-1
@@ -203,7 +203,7 @@ class FlamechartStackLayerView extends View {
203
);
204
205
if (trimmedName !== null) {
206
- context.fillStyle = COLORS.PRIORITY_LABEL;
206
+ context.fillStyle = COLORS.FLAME_GRAPH_LABEL;
207
208
// Prevent text from being drawn outside `viewableArea`
209
const textOverflowsViewableArea = !rectEqualToRect(
packages/react-devtools-scheduling-profiler/src/content-views/constants.js
+126
-31
@@ -41,34 +41,129 @@ export const FLAMECHART_FONT_SIZE = 10;
41
export const FLAMECHART_FRAME_HEIGHT = 16;
42
export const FLAMECHART_TEXT_PADDING = 3;
43
44
-export const COLORS = Object.freeze({
45
- BACKGROUND: '#ffffff',
46
- PRIORITY_BACKGROUND: '#ededf0',
47
- PRIORITY_BORDER: '#d7d7db',
48
- PRIORITY_LABEL: '#272727',
49
- USER_TIMING: '#c9cacd',
50
- USER_TIMING_HOVER: '#93959a',
51
- REACT_IDLE: '#edf6ff',
52
- REACT_IDLE_SELECTED: '#EDF6FF',
53
- REACT_IDLE_HOVER: '#EDF6FF',
54
- REACT_RENDER: '#9fc3f3',
55
- REACT_RENDER_SELECTED: '#64A9F5',
56
- REACT_RENDER_HOVER: '#2683E2',
57
- REACT_COMMIT: '#ff718e',
58
- REACT_COMMIT_SELECTED: '#FF5277',
59
- REACT_COMMIT_HOVER: '#ed0030',
60
- REACT_LAYOUT_EFFECTS: '#c88ff0',
61
- REACT_LAYOUT_EFFECTS_SELECTED: '#934FC1',
62
- REACT_LAYOUT_EFFECTS_HOVER: '#601593',
63
- REACT_PASSIVE_EFFECTS: '#c88ff0',
64
- REACT_PASSIVE_EFFECTS_SELECTED: '#934FC1',
65
- REACT_PASSIVE_EFFECTS_HOVER: '#601593',
66
- REACT_SCHEDULE: '#9fc3f3',
67
- REACT_SCHEDULE_HOVER: '#2683E2',
68
- REACT_SCHEDULE_CASCADING: '#ff718e',
69
- REACT_SCHEDULE_CASCADING_HOVER: '#ed0030',
70
- REACT_SUSPEND: '#a6e59f',
71
- REACT_SUSPEND_HOVER: '#13bc00',
72
- REACT_WORK_BORDER: '#ffffff',
73
- TIME_MARKER_LABEL: '#18212b',
74
-});
44
+// TODO Replace this with "export let" vars
45
+export let COLORS = {
46
+ BACKGROUND: '',
47
+ PRIORITY_BACKGROUND: '',
48
+ PRIORITY_BORDER: '',
49
+ PRIORITY_LABEL: '',
50
+ FLAME_GRAPH_LABEL: '',
51
+ USER_TIMING: '',
52
+ USER_TIMING_HOVER: '',
53
+ REACT_IDLE: '',
54
+ REACT_IDLE_SELECTED: '',
55
+ REACT_IDLE_HOVER: '',
56
+ REACT_RENDER: '',
57
+ REACT_RENDER_SELECTED: '',
58
+ REACT_RENDER_HOVER: '',
59
+ REACT_COMMIT: '',
60
+ REACT_COMMIT_SELECTED: '',
61
+ REACT_COMMIT_HOVER: '',
62
+ REACT_LAYOUT_EFFECTS: '',
63
+ REACT_LAYOUT_EFFECTS_SELECTED: '',
64
+ REACT_LAYOUT_EFFECTS_HOVER: '',
65
+ REACT_PASSIVE_EFFECTS: '',
66
+ REACT_PASSIVE_EFFECTS_SELECTED: '',
67
+ REACT_PASSIVE_EFFECTS_HOVER: '',
68
+ REACT_RESIZE_BAR: '',
69
+ REACT_SCHEDULE: '',
70
+ REACT_SCHEDULE_HOVER: '',
71
+ REACT_SCHEDULE_CASCADING: '',
72
+ REACT_SCHEDULE_CASCADING_HOVER: '',
73
+ REACT_SUSPEND: '',
74
+ REACT_SUSPEND_HOVER: '',
75
+ REACT_WORK_BORDER: '',
76
+ TIME_MARKER_LABEL: '',
77
+};
78
+
79
+export function updateColorsToMatchTheme(): void {
80
+ const computedStyle = getComputedStyle((document.body: any));
81
+
82
+ COLORS = {
83
+ BACKGROUND: computedStyle.getPropertyValue('--color-background'),
84
+ PRIORITY_BACKGROUND: computedStyle.getPropertyValue(
85
+ '--color-scheduling-profiler-priority-background',
86
+ ),
87
+ PRIORITY_BORDER: computedStyle.getPropertyValue(
88
+ '--color-scheduling-profiler-priority-border',
89
+ ),
90
+ PRIORITY_LABEL: computedStyle.getPropertyValue('--color-text'),
91
+ FLAME_GRAPH_LABEL: computedStyle.getPropertyValue(
92
+ '--color-scheduling-profiler-flame-graph-label',
93
+ ),
94
+ USER_TIMING: computedStyle.getPropertyValue(
95
+ '--color-scheduling-profiler-user-timing',
96
+ ),
97
+ USER_TIMING_HOVER: computedStyle.getPropertyValue(
98
+ '--color-scheduling-profiler-user-timing-hover',
99
+ ),
100
+ REACT_IDLE: computedStyle.getPropertyValue(
101
+ '--color-scheduling-profiler-react-idle',
102
+ ),
103
+ REACT_IDLE_SELECTED: computedStyle.getPropertyValue(
104
+ '--color-scheduling-profiler-react-idle-selected',
105
+ ),
106
+ REACT_IDLE_HOVER: computedStyle.getPropertyValue(
107
+ '--color-scheduling-profiler-react-idle-hover',
108
+ ),
109
+ REACT_RENDER: computedStyle.getPropertyValue(
110
+ '--color-scheduling-profiler-react-render',
111
+ ),
112
+ REACT_RENDER_SELECTED: computedStyle.getPropertyValue(
113
+ '--color-scheduling-profiler-react-render-selected',
114
+ ),
115
+ REACT_RENDER_HOVER: computedStyle.getPropertyValue(
116
+ '--color-scheduling-profiler-react-render-hover',
117
+ ),
118
+ REACT_COMMIT: computedStyle.getPropertyValue(
119
+ '--color-scheduling-profiler-react-commit',
120
+ ),
121
+ REACT_COMMIT_SELECTED: computedStyle.getPropertyValue(
122
+ '--color-scheduling-profiler-react-commit-selected',
123
+ ),
124
+ REACT_COMMIT_HOVER: computedStyle.getPropertyValue(
125
+ '--color-scheduling-profiler-react-commit-hover',
126
+ ),
127
+ REACT_LAYOUT_EFFECTS: computedStyle.getPropertyValue(
128
+ '--color-scheduling-profiler-react-layout-effects',
129
+ ),
130
+ REACT_LAYOUT_EFFECTS_SELECTED: computedStyle.getPropertyValue(
131
+ '--color-scheduling-profiler-react-layout-effects-selected',
132
+ ),
133
+ REACT_LAYOUT_EFFECTS_HOVER: computedStyle.getPropertyValue(
134
+ '--color-scheduling-profiler-react-layout-effects-hover',
135
+ ),
136
+ REACT_PASSIVE_EFFECTS: computedStyle.getPropertyValue(
137
+ '--color-scheduling-profiler-react-passive-effects',
138
+ ),
139
+ REACT_PASSIVE_EFFECTS_SELECTED: computedStyle.getPropertyValue(
140
+ '--color-scheduling-profiler-react-passive-effects-selected',
141
+ ),
142
+ REACT_PASSIVE_EFFECTS_HOVER: computedStyle.getPropertyValue(
143
+ '--color-scheduling-profiler-react-passive-effects-hover',
144
+ ),
145
+ REACT_RESIZE_BAR: computedStyle.getPropertyValue('--color-resize-bar'),
146
+ REACT_SCHEDULE: computedStyle.getPropertyValue(
147
+ '--color-scheduling-profiler-react-schedule',
148
+ ),
149
+ REACT_SCHEDULE_HOVER: computedStyle.getPropertyValue(
150
+ '--color-scheduling-profiler-react-schedule-hover',
151
+ ),
152
+ REACT_SCHEDULE_CASCADING: computedStyle.getPropertyValue(
153
+ '--color-scheduling-profiler-react-schedule-cascading',
154
+ ),
155
+ REACT_SCHEDULE_CASCADING_HOVER: computedStyle.getPropertyValue(
156
+ '--color-scheduling-profiler-react-schedule-cascading-hover',
157
+ ),
158
+ REACT_SUSPEND: computedStyle.getPropertyValue(
159
+ '--color-scheduling-profiler-react-suspend',
160
+ ),
161
+ REACT_SUSPEND_HOVER: computedStyle.getPropertyValue(
162
+ '--color-scheduling-profiler-react-suspend-hover',
163
+ ),
164
+ REACT_WORK_BORDER: computedStyle.getPropertyValue(
165
+ '--color-scheduling-profiler-react-work-border',
166
+ ),
167
+ TIME_MARKER_LABEL: computedStyle.getPropertyValue('--color-text'),
168
+ };
169
+}
packages/react-devtools-scheduling-profiler/src/context/ContextMenu.css
deleted
-10
@@ -1,10 +0,0 @@
1
-.ContextMenu {
2
- position: absolute;
3
- border-radius: 0.125rem;
4
- background-color: #ffffff;
5
- border: 1px solid #ccc;
6
- box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
7
- font-size: 11px;
8
- overflow: hidden;
9
- z-index: 10000002;
10
-}
packages/react-devtools-scheduling-profiler/src/context/ContextMenu.js
deleted
-143
@@ -1,143 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-import type {RegistryContextType} from './Contexts';
11
-
12
-import * as React from 'react';
13
-import {useContext, useEffect, useLayoutEffect, useRef, useState} from 'react';
14
-import {createPortal} from 'react-dom';
15
-import {RegistryContext} from './Contexts';
16
-
17
-import styles from './ContextMenu.css';
18
-
19
-function repositionToFit(element: HTMLElement, pageX: number, pageY: number) {
20
- const ownerWindow = element.ownerDocument.defaultView;
21
- if (element !== null) {
22
- if (pageY + element.offsetHeight >= ownerWindow.innerHeight) {
23
- if (pageY - element.offsetHeight > 0) {
24
- element.style.top = `${pageY - element.offsetHeight}px`;
25
- } else {
26
- element.style.top = '0px';
27
- }
28
- } else {
29
- element.style.top = `${pageY}px`;
30
- }
31
-
32
- if (pageX + element.offsetWidth >= ownerWindow.innerWidth) {
33
- if (pageX - element.offsetWidth > 0) {
34
- element.style.left = `${pageX - element.offsetWidth}px`;
35
- } else {
36
- element.style.left = '0px';
37
- }
38
- } else {
39
- element.style.left = `${pageX}px`;
40
- }
41
- }
42
-}
43
-
44
-const HIDDEN_STATE = {
45
- data: null,
46
- isVisible: false,
47
- pageX: 0,
48
- pageY: 0,
49
-};
50
-
51
-type Props = {|
52
- children: (data: Object) => React$Node,
53
- id: string,
54
-|};
55
-
56
-export default function ContextMenu({children, id}: Props) {
57
- const {hideMenu, registerMenu} = useContext<RegistryContextType>(
58
- RegistryContext,
59
- );
60
-
61
- const [state, setState] = useState(HIDDEN_STATE);
62
-
63
- const bodyAccessorRef = useRef<HTMLDivElement | null>(null);
64
- const containerRef = useRef<HTMLDivElement | null>(null);
65
- const menuRef = useRef<HTMLDivElement | null>(null);
66
-
67
- useEffect(() => {
68
- if (!bodyAccessorRef.current) {
69
- return;
70
- }
71
- const ownerDocument = bodyAccessorRef.current.ownerDocument;
72
- containerRef.current = ownerDocument.createElement('div');
73
- if (ownerDocument.body) {
74
- ownerDocument.body.appendChild(containerRef.current);
75
- }
76
- return () => {
77
- if (ownerDocument.body && containerRef.current) {
78
- ownerDocument.body.removeChild(containerRef.current);
79
- }
80
- };
81
- }, [bodyAccessorRef, containerRef]);
82
-
83
- useEffect(() => {
84
- const showMenuFn = ({data, pageX, pageY}) => {
85
- setState({data, isVisible: true, pageX, pageY});
86
- };
87
- const hideMenuFn = () => setState(HIDDEN_STATE);
88
- return registerMenu(id, showMenuFn, hideMenuFn);
89
- }, [id]);
90
-
91
- useLayoutEffect(() => {
92
- if (!state.isVisible || !containerRef.current) {
93
- return;
94
- }
95
-
96
- const menu = menuRef.current;
97
- if (!menu) {
98
- return;
99
- }
100
-
101
- const hideUnlessContains: MouseEventHandler &
102
- TouchEventHandler &
103
- KeyboardEventHandler = event => {
104
- if (event.target instanceof HTMLElement && !menu.contains(event.target)) {
105
- hideMenu();
106
- }
107
- };
108
-
109
- const ownerDocument = containerRef.current.ownerDocument;
110
- ownerDocument.addEventListener('mousedown', hideUnlessContains);
111
- ownerDocument.addEventListener('touchstart', hideUnlessContains);
112
- ownerDocument.addEventListener('keydown', hideUnlessContains);
113
-
114
- const ownerWindow = ownerDocument.defaultView;
115
- ownerWindow.addEventListener('resize', hideMenu);
116
-
117
- repositionToFit(menu, state.pageX, state.pageY);
118
-
119
- return () => {
120
- ownerDocument.removeEventListener('mousedown', hideUnlessContains);
121
- ownerDocument.removeEventListener('touchstart', hideUnlessContains);
122
- ownerDocument.removeEventListener('keydown', hideUnlessContains);
123
-
124
- ownerWindow.removeEventListener('resize', hideMenu);
125
- };
126
- }, [state]);
127
-
128
- if (!state.isVisible) {
129
- return <div ref={bodyAccessorRef} />;
130
- } else {
131
- const container = containerRef.current;
132
- if (container !== null) {
133
- return createPortal(
134
- <div ref={menuRef} className={styles.ContextMenu}>
135
- {children(state.data)}
136
- </div>,
137
- container,
138
- );
139
- } else {
140
- return null;
141
- }
142
- }
143
-}
packages/react-devtools-scheduling-profiler/src/context/ContextMenuItem.css
deleted
-20
@@ -1,20 +0,0 @@
1
-.ContextMenuItem {
2
- display: flex;
3
- align-items: center;
4
- color: #333;
5
- padding: 0.5rem 0.75rem;
6
- cursor: default;
7
- border-top: 1px solid #ccc;
8
-}
9
-.ContextMenuItem:first-of-type {
10
- border-top: none;
11
-}
12
-.ContextMenuItem:hover,
13
-.ContextMenuItem:focus {
14
- outline: 0;
15
- background-color: rgba(0, 136, 250, 0.1);
16
-}
17
-.ContextMenuItem:active {
18
- background-color: #0088fa;
19
- color: #fff;
20
-}
packages/react-devtools-scheduling-profiler/src/context/ContextMenuItem.js
deleted
-40
@@ -1,40 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-import type {RegistryContextType} from './Contexts';
11
-
12
-import * as React from 'react';
13
-import {useContext} from 'react';
14
-import {RegistryContext} from './Contexts';
15
-
16
-import styles from './ContextMenuItem.css';
17
-
18
-type Props = {|
19
- children: React$Node,
20
- onClick: () => void,
21
- title: string,
22
-|};
23
-
24
-export default function ContextMenuItem({children, onClick, title}: Props) {
25
- const {hideMenu} = useContext<RegistryContextType>(RegistryContext);
26
-
27
- const handleClick: MouseEventHandler = event => {
28
- onClick();
29
- hideMenu();
30
- };
31
-
32
- return (
33
- <div
34
- className={styles.ContextMenuItem}
35
- onClick={handleClick}
36
- onTouchEnd={handleClick}>
37
- {children}
38
- </div>
39
- );
40
-}
packages/react-devtools-scheduling-profiler/src/context/Contexts.js
deleted
-87
@@ -1,87 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-import {createContext} from 'react';
11
-
12
-export type ShowFn = ({|data: Object, pageX: number, pageY: number|}) => void;
13
-export type HideFn = () => void;
14
-export type OnChangeFn = boolean => void;
15
-
16
-const idToShowFnMap = new Map<string, ShowFn>();
17
-const idToHideFnMap = new Map<string, HideFn>();
18
-
19
-let currentHideFn: ?HideFn = null;
20
-let currentOnChange: ?OnChangeFn = null;
21
-
22
-function hideMenu() {
23
- if (typeof currentHideFn === 'function') {
24
- currentHideFn();
25
-
26
- if (typeof currentOnChange === 'function') {
27
- currentOnChange(false);
28
- }
29
- }
30
-
31
- currentHideFn = null;
32
- currentOnChange = null;
33
-}
34
-
35
-function showMenu({
36
- data,
37
- id,
38
- onChange,
39
- pageX,
40
- pageY,
41
-}: {|
42
- data: Object,
43
- id: string,
44
- onChange?: OnChangeFn,
45
- pageX: number,
46
- pageY: number,
47
-|}) {
48
- const showFn = idToShowFnMap.get(id);
49
- if (typeof showFn === 'function') {
50
- // Prevent open menus from being left hanging.
51
- hideMenu();
52
-
53
- currentHideFn = idToHideFnMap.get(id);
54
- showFn({data, pageX, pageY});
55
-
56
- if (typeof onChange === 'function') {
57
- currentOnChange = onChange;
58
- onChange(true);
59
- }
60
- }
61
-}
62
-
63
-function registerMenu(id: string, showFn: ShowFn, hideFn: HideFn) {
64
- if (idToShowFnMap.has(id)) {
65
- throw Error(`Context menu with id "${id}" already registered.`);
66
- }
67
-
68
- idToShowFnMap.set(id, showFn);
69
- idToHideFnMap.set(id, hideFn);
70
-
71
- return function unregisterMenu() {
72
- idToShowFnMap.delete(id);
73
- idToHideFnMap.delete(id);
74
- };
75
-}
76
-
77
-export type RegistryContextType = {|
78
- hideMenu: typeof hideMenu,
79
- showMenu: typeof showMenu,
80
- registerMenu: typeof registerMenu,
81
-|};
82
-
83
-export const RegistryContext = createContext<RegistryContextType>({
84
- hideMenu,
85
- showMenu,
86
- registerMenu,
87
-});
packages/react-devtools-scheduling-profiler/src/context/index.js
deleted
-15
@@ -1,15 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-import {RegistryContext} from './Contexts';
11
-import ContextMenu from './ContextMenu';
12
-import ContextMenuItem from './ContextMenuItem';
13
-import useContextMenu from './useContextMenu';
14
-
15
-export {RegistryContext, ContextMenu, ContextMenuItem, useContextMenu};
packages/react-devtools-scheduling-profiler/src/context/useContextMenu.js
deleted
-52
@@ -1,52 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-import type {OnChangeFn, RegistryContextType} from './Contexts';
11
-
12
-import {useContext, useEffect} from 'react';
13
-import {RegistryContext} from './Contexts';
14
-
15
-export default function useContextMenu<T>({
16
- data,
17
- id,
18
- onChange,
19
- ref,
20
-}: {|
21
- data: T,
22
- id: string,
23
- onChange: OnChangeFn,
24
- ref: {+current: HTMLElement | null},
25
-|}) {
26
- const {showMenu} = useContext<RegistryContextType>(RegistryContext);
27
-
28
- useEffect(() => {
29
- if (ref.current !== null) {
30
- const handleContextMenu = (event: MouseEvent | TouchEvent) => {
31
- event.preventDefault();
32
- event.stopPropagation();
33
-
34
- const pageX =
35
- (event: any).pageX ||
36
- (event.touches && (event: any).touches[0].pageX);
37
- const pageY =
38
- (event: any).pageY ||
39
- (event.touches && (event: any).touches[0].pageY);
40
-
41
- showMenu({data, id, onChange, pageX, pageY});
42
- };
43
-
44
- const trigger = ref.current;
45
- trigger.addEventListener('contextmenu', handleContextMenu);
46
-
47
- return () => {
48
- trigger.removeEventListener('contextmenu', handleContextMenu);
49
- };
50
- }
51
- }, [data, id, showMenu]);
52
-}
packages/react-devtools-scheduling-profiler/src/createDataResourceFromImportedFile.js
new
+46
@@ -0,0 +1,46 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+import {createResource} from 'react-devtools-shared/src/devtools/cache';
11
+import {importFile} from './import-worker';
12
+
13
+import type {Resource} from 'react-devtools-shared/src/devtools/cache';
14
+import type {ReactProfilerData} from './types';
15
+import type {ImportWorkerOutputData} from './import-worker/index';
16
+
17
+export type DataResource = Resource<void, File, ReactProfilerData | Error>;
18
+
19
+export default function createDataResourceFromImportedFile(
20
+ file: File,
21
+): DataResource {
22
+ return createResource(
23
+ () => {
24
+ return new Promise<ReactProfilerData | Error>((resolve, reject) => {
25
+ const promise = ((importFile(
26
+ file,
27
+ ): any): Promise<ImportWorkerOutputData>);
28
+ promise.then(data => {
29
+ switch (data.status) {
30
+ case 'SUCCESS':
31
+ resolve(data.processedData);
32
+ break;
33
+ case 'INVALID_PROFILE_ERROR':
34
+ resolve(data.error);
35
+ break;
36
+ case 'UNEXPECTED_ERROR':
37
+ reject(data.error);
38
+ break;
39
+ }
40
+ });
41
+ });
42
+ },
43
+ () => file,
44
+ {useWeakMap: true},
45
+ );
46
+}
packages/react-devtools-scheduling-profiler/src/hooks.js
deleted
-71
@@ -1,71 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-import {
11
- // $FlowFixMe
12
- unstable_createMutableSource as createMutableSource,
13
- useLayoutEffect,
14
- // $FlowFixMe
15
- unstable_useMutableSource as useMutableSource,
16
-} from 'react';
17
-
18
-import {
19
- updateDisplayDensity,
20
- updateThemeVariables,
21
-} from 'react-devtools-shared/src/devtools/views/Settings/SettingsContext';
22
-import {enableDarkMode} from './SchedulingProfilerFeatureFlags';
23
-
24
-export type BrowserTheme = 'dark' | 'light';
25
-
26
-const DARK_MODE_QUERY = '(prefers-color-scheme: dark)';
27
-
28
-const getSnapshot = window =>
29
- window.matchMedia(DARK_MODE_QUERY).matches ? 'dark' : 'light';
30
-
31
-const darkModeMutableSource = createMutableSource(
32
- window,
33
- () => window.matchMedia(DARK_MODE_QUERY).matches,
34
-);
35
-
36
-const subscribe = (window, callback) => {
37
- const mediaQueryList = window.matchMedia(DARK_MODE_QUERY);
38
- mediaQueryList.addEventListener('change', callback);
39
- return () => {
40
- mediaQueryList.removeEventListener('change', callback);
41
- };
42
-};
43
-
44
-export function useBrowserTheme(): void {
45
- const theme = useMutableSource(darkModeMutableSource, getSnapshot, subscribe);
46
-
47
- useLayoutEffect(() => {
48
- const documentElements = [((document.documentElement: any): HTMLElement)];
49
- if (enableDarkMode) {
50
- switch (theme) {
51
- case 'light':
52
- updateThemeVariables('light', documentElements);
53
- break;
54
- case 'dark':
55
- updateThemeVariables('dark', documentElements);
56
- break;
57
- default:
58
- throw Error(`Unsupported theme value "${theme}"`);
59
- }
60
- } else {
61
- updateThemeVariables('light', documentElements);
62
- }
63
- }, [theme]);
64
-}
65
-
66
-export function useDisplayDensity(): void {
67
- useLayoutEffect(() => {
68
- const documentElements = [((document.documentElement: any): HTMLElement)];
69
- updateDisplayDensity('comfortable', documentElements);
70
- }, []);
71
-}
packages/react-devtools-scheduling-profiler/src/import-worker/importFile.js
renamed
+9
-20
@@ -10,7 +10,7 @@
10
import 'regenerator-runtime/runtime';
11
12
import type {TimelineEvent} from '@elg/speedscope';
13
-import type {ReactProfilerData} from '../types';
13
+import type {ImportWorkerOutputData} from './index';
14
15
import preprocessData from './preprocessData';
16
import {readInputData} from './readInputData';
@@ -18,18 +18,7 @@ import InvalidProfileError from './InvalidProfileError';
18
19
declare var self: DedicatedWorkerGlobalScope;
20
21
-type ImportWorkerInputData = {|
22
- file: File,
23
-|};
24
-
25
-export type ImportWorkerOutputData =
26
- | {|status: 'SUCCESS', processedData: ReactProfilerData|}
27
- | {|status: 'INVALID_PROFILE_ERROR', error: Error|}
28
- | {|status: 'UNEXPECTED_ERROR', error: Error|};
29
-
30
-self.onmessage = async function(event: MessageEvent) {
31
- const {file} = ((event.data: any): ImportWorkerInputData);
32
-
21
+export async function importFile(file: File): Promise<ImportWorkerOutputData> {
22
try {
23
const readFile = await readInputData(file);
24
const events: TimelineEvent[] = JSON.parse(readFile);
@@ -37,21 +26,21 @@ self.onmessage = async function(event: MessageEvent) {
26
throw new InvalidProfileError('No profiling data found in file.');
27
}
28
40
- self.postMessage({
29
+ return {
30
status: 'SUCCESS',
31
processedData: preprocessData(events),
43
- });
32
+ };
33
} catch (error) {
34
if (error instanceof InvalidProfileError) {
46
- self.postMessage({
35
+ return {
36
status: 'INVALID_PROFILE_ERROR',
37
error,
49
- });
38
+ };
39
} else {
51
- self.postMessage({
40
+ return {
41
status: 'UNEXPECTED_ERROR',
42
error,
54
- });
43
+ };
44
}
45
}
57
-};
46
+}
packages/react-devtools-scheduling-profiler/src/import-worker/importFile.worker.js
renamed
+3
-3
@@ -3,8 +3,8 @@
3
*
4
* This source code is licensed under the MIT license found in the
5
* LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
6
*/
7
10
-export const enableDarkMode = false;
8
+import * as importFileModule from './importFile';
9
+
10
+export const importFile = importFileModule.importFile;
packages/react-devtools-scheduling-profiler/src/import-worker/index.js
new
+31
@@ -0,0 +1,31 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+// This file uses workerize to load ./importFile.worker as a webworker and instanciates it,
11
+// exposing flow typed functions that can be used on other files.
12
+
13
+import * as importFileModule from './importFile';
14
+import WorkerizedImportFile from './importFile.worker';
15
+
16
+import type {ReactProfilerData} from '../types';
17
+
18
+type ImportFileModule = typeof importFileModule;
19
+
20
+const workerizedImportFile: ImportFileModule = window.Worker
21
+ ? WorkerizedImportFile()
22
+ : importFileModule;
23
+
24
+export type ImportWorkerOutputData =
25
+ | {|status: 'SUCCESS', processedData: ReactProfilerData|}
26
+ | {|status: 'INVALID_PROFILE_ERROR', error: Error|}
27
+ | {|status: 'UNEXPECTED_ERROR', error: Error|};
28
+
29
+export type importFileFunction = (file: File) => ImportWorkerOutputData;
30
+
31
+export const importFile = (file: File) => workerizedImportFile.importFile(file);
packages/react-devtools-scheduling-profiler/src/index.css
deleted
-21
@@ -1,21 +0,0 @@
1
-html {
2
- height: 100%;
3
-}
4
-
5
-body {
6
- height: 100%;
7
- margin: 0;
8
- font-family: var(--font-family-sans);
9
- font-size: var(--font-size-sans-normal);
10
- background-color: var(--color-background);
11
- color: var(--color-text);
12
-}
13
-
14
-code {
15
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
16
- monospace;
17
-}
18
-
19
-.Container {
20
- height: 100%;
21
-}
\ No newline at end of file
packages/react-devtools-scheduling-profiler/src/index.js
deleted
-31
@@ -1,31 +0,0 @@
1
-/**
2
- * Copyright (c) Facebook, Inc. and its affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-import 'regenerator-runtime/runtime';
11
-
12
-import * as React from 'react';
13
-// $FlowFixMe Flow does not yet know about createRoot()
14
-import {createRoot} from 'react-dom';
15
-import nullthrows from 'nullthrows';
16
-import App from './App';
17
-
18
-import styles from './index.css';
19
-
20
-const container = document.createElement('div');
21
-container.className = styles.Container;
22
-container.id = 'root';
23
-
24
-const body = nullthrows(document.body, 'Expect document.body to exist');
25
-body.appendChild(container);
26
-
27
-createRoot(container).render(
28
- <React.StrictMode>
29
- <App />
30
- </React.StrictMode>,
31
-);
packages/react-devtools-scheduling-profiler/src/view-base/ResizableSplitView.js
+18
-4
@@ -15,6 +15,7 @@ import type {
15
} from './useCanvasInteraction';
16
import type {Rect, Size} from './geometry';
17
18
+import {COLORS} from '../content-views/constants';
19
import nullthrows from 'nullthrows';
20
import {Surface} from './Surface';
21
import {View} from './View';
@@ -38,14 +39,11 @@ type LayoutState = $ReadOnly<{|
39
|}>;
40
41
function getColorForBarState(state: ResizeBarState): string {
41
- // Colors obtained from Firefox Profiler
42
switch (state) {
43
case 'normal':
44
- return '#ccc';
44
case 'hovered':
46
- return '#bbb';
45
case 'dragging':
48
- return '#aaa';
46
+ return COLORS.REACT_RESIZE_BAR;
47
}
48
throw new Error(`Unknown resize bar state ${state}`);
49
}
@@ -131,6 +129,7 @@ class ResizeBar extends View {
129
}
130
131
export class ResizableSplitView extends View {
132
+ _canvasRef: {current: HTMLCanvasElement | null};
133
_resizingState: ResizingState | null = null;
134
_layoutState: LayoutState;
135
@@ -139,9 +138,12 @@ export class ResizableSplitView extends View {
138
frame: Rect,
139
topSubview: View,
140
bottomSubview: View,
141
+ canvasRef: {current: HTMLCanvasElement | null},
142
) {
143
super(surface, frame, noopLayout);
144
145
+ this._canvasRef = canvasRef;
146
+
147
this.addSubview(topSubview);
148
this.addSubview(new ResizeBar(surface, frame));
149
this.addSubview(bottomSubview);
@@ -279,6 +281,18 @@ export class ResizableSplitView extends View {
281
}
282
283
_handleMouseMove(interaction: MouseMoveInteraction) {
284
+ const cursorLocation = interaction.payload.location;
285
+ const resizeBarFrame = this._getResizeBar().frame;
286
+
287
+ const canvas = this._canvasRef.current;
288
+ if (canvas !== null) {
289
+ if (rectContainsPoint(cursorLocation, resizeBarFrame)) {
290
+ canvas.style.cursor = 'ns-resize';
291
+ } else {
292
+ canvas.style.cursor = 'default';
293
+ }
294
+ }
295
+
296
const {_resizingState} = this;
297
if (_resizingState) {
298
this._resizingState = {
packages/react-devtools-scheduling-profiler/src/view-base/useCanvasInteraction.js
+5
-4
@@ -175,15 +175,16 @@ export function useCanvasInteraction(
175
return false;
176
};
177
178
- document.addEventListener('mousemove', onDocumentMouseMove);
179
- document.addEventListener('mouseup', onDocumentMouseUp);
178
+ const ownerDocument = canvas.ownerDocument;
179
+ ownerDocument.addEventListener('mousemove', onDocumentMouseMove);
180
+ ownerDocument.addEventListener('mouseup', onDocumentMouseUp);
181
182
canvas.addEventListener('mousedown', onCanvasMouseDown);
183
canvas.addEventListener('wheel', onCanvasWheel);
184
185
return () => {
185
- document.removeEventListener('mousemove', onDocumentMouseMove);
186
- document.removeEventListener('mouseup', onDocumentMouseUp);
186
+ ownerDocument.removeEventListener('mousemove', onDocumentMouseMove);
187
+ ownerDocument.removeEventListener('mouseup', onDocumentMouseUp);
188
189
canvas.removeEventListener('mousedown', onCanvasMouseDown);
190
canvas.removeEventListener('wheel', onCanvasWheel);
packages/react-devtools-scheduling-profiler/vercel.json
deleted
-3
@@ -1,3 +0,0 @@
1
-{
2
- "name": "react-devtools-scheduling-profiler"
3
-}
packages/react-devtools-scheduling-profiler/webpack.config.js
deleted
-128
@@ -1,128 +0,0 @@
1
-'use strict';
2
-
3
-const {resolve} = require('path');
4
-const {DefinePlugin} = require('webpack');
5
-const HtmlWebpackPlugin = require('html-webpack-plugin');
6
-const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
7
-const {getVersionString} = require('./buildUtils');
8
-
9
-const NODE_ENV = process.env.NODE_ENV;
10
-if (!NODE_ENV) {
11
- console.error('NODE_ENV not set');
12
- process.exit(1);
13
-}
14
-const __DEV__ = NODE_ENV === 'development';
15
-
16
-const TARGET = process.env.TARGET;
17
-if (!TARGET) {
18
- console.error('TARGET not set');
19
- process.exit(1);
20
-}
21
-const shouldUseDevServer = TARGET === 'local';
22
-
23
-const builtModulesDir = resolve(__dirname, '..', '..', 'build', 'node_modules');
24
-
25
-const DEVTOOLS_VERSION = getVersionString();
26
-
27
-const imageInlineSizeLimit = 10000;
28
-
29
-const babelOptions = {
30
- configFile: resolve(
31
- __dirname,
32
- '..',
33
- 'react-devtools-shared',
34
- 'babel.config.js',
35
- ),
36
- plugins: shouldUseDevServer
37
- ? [resolve(builtModulesDir, 'react-refresh/babel')]
38
- : [],
39
-};
40
-
41
-const config = {
42
- mode: __DEV__ ? 'development' : 'production',
43
- devtool: __DEV__ ? 'cheap-module-eval-source-map' : 'source-map',
44
- entry: {
45
- app: './src/index.js',
46
- },
47
- resolve: {
48
- alias: {
49
- react: resolve(builtModulesDir, 'react'),
50
- 'react-dom': resolve(builtModulesDir, 'react-dom'),
51
- 'react-refresh': resolve(builtModulesDir, 'react-refresh'),
52
- scheduler: resolve(builtModulesDir, 'scheduler'),
53
- },
54
- },
55
- plugins: [
56
- new DefinePlugin({
57
- __DEV__,
58
- __PROFILE__: false,
59
- __EXPERIMENTAL__: true,
60
- __VARIANT__: false,
61
- 'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
62
- }),
63
- new HtmlWebpackPlugin({
64
- title: 'React Concurrent Mode Profiler',
65
- }),
66
- shouldUseDevServer && new ReactRefreshWebpackPlugin(),
67
- ].filter(Boolean),
68
- module: {
69
- rules: [
70
- {
71
- test: /\.worker\.js$/,
72
- use: [
73
- 'worker-loader',
74
- {
75
- loader: 'babel-loader',
76
- options: babelOptions,
77
- },
78
- ],
79
- },
80
- {
81
- test: /\.js$/,
82
- loader: 'babel-loader',
83
- options: babelOptions,
84
- },
85
- {
86
- test: /\.css$/,
87
- use: [
88
- {
89
- loader: 'style-loader',
90
- },
91
- {
92
- loader: 'css-loader',
93
- options: {
94
- sourceMap: true,
95
- modules: {
96
- localIdentName: '[local]___[hash:base64:5]',
97
- },
98
- },
99
- },
100
- ],
101
- },
102
- {
103
- test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
104
- loader: 'url-loader',
105
- options: {
106
- limit: imageInlineSizeLimit,
107
- name: 'static/media/[name].[hash:8].[ext]',
108
- },
109
- },
110
- ],
111
- },
112
-};
113
-
114
-if (shouldUseDevServer) {
115
- config.devServer = {
116
- hot: true,
117
- port: 8081,
118
- clientLogLevel: 'warning',
119
- stats: 'errors-only',
120
- };
121
-} else {
122
- config.output = {
123
- path: resolve(__dirname, 'dist'),
124
- filename: '[name].js',
125
- };
126
-}
127
-
128
-module.exports = config;
packages/react-devtools-shared/src/devtools/ContextMenu/ContextMenu.css
+2
@@ -1,7 +1,9 @@
1
.ContextMenu {
2
position: absolute;
3
background-color: var(--color-context-background);
4
+ box-shadow: 1px 1px 2px var(--color-shadow);
5
border-radius: 0.25rem;
6
overflow: hidden;
7
z-index: 10000002;
8
+ user-select: none;
9
}
\ No newline at end of file
packages/react-devtools-shared/src/devtools/ContextMenu/ContextMenu.js
+9
-11
@@ -54,7 +54,9 @@ type Props = {|
54
|};
55
56
export default function ContextMenu({children, id}: Props) {
57
- const {registerMenu} = useContext<RegistryContextType>(RegistryContext);
57
+ const {hideMenu, registerMenu} = useContext<RegistryContextType>(
58
+ RegistryContext,
59
+ );
60
61
const [state, setState] = useState(HIDDEN_STATE);
62
@@ -75,11 +77,11 @@ export default function ContextMenu({children, id}: Props) {
77
}, []);
78
79
useEffect(() => {
78
- const showMenu = ({data, pageX, pageY}) => {
80
+ const showMenuFn = ({data, pageX, pageY}) => {
81
setState({data, isVisible: true, pageX, pageY});
82
};
81
- const hideMenu = () => setState(HIDDEN_STATE);
82
- return registerMenu(id, showMenu, hideMenu);
83
+ const hideMenuFn = () => setState(HIDDEN_STATE);
84
+ return registerMenu(id, showMenuFn, hideMenuFn);
85
}, [id]);
86
87
useLayoutEffect(() => {
@@ -92,21 +94,17 @@ export default function ContextMenu({children, id}: Props) {
94
if (container !== null) {
95
const hideUnlessContains = event => {
96
if (!menu.contains(event.target)) {
95
- setState(HIDDEN_STATE);
97
+ hideMenu();
98
}
99
};
100
99
- const hide = event => {
100
- setState(HIDDEN_STATE);
101
- };
102
-
101
const ownerDocument = container.ownerDocument;
102
ownerDocument.addEventListener('mousedown', hideUnlessContains);
103
ownerDocument.addEventListener('touchstart', hideUnlessContains);
104
ownerDocument.addEventListener('keydown', hideUnlessContains);
105
106
const ownerWindow = ownerDocument.defaultView;
109
- ownerWindow.addEventListener('resize', hide);
107
+ ownerWindow.addEventListener('resize', hideMenu);
108
109
repositionToFit(menu, state.pageX, state.pageY);
110
@@ -115,7 +113,7 @@ export default function ContextMenu({children, id}: Props) {
113
ownerDocument.removeEventListener('touchstart', hideUnlessContains);
114
ownerDocument.removeEventListener('keydown', hideUnlessContains);
115
118
- ownerWindow.removeEventListener('resize', hide);
116
+ ownerWindow.removeEventListener('resize', hideMenu);
117
};
118
}
119
}, [state]);
packages/react-devtools-shared/src/devtools/ContextMenu/Contexts.js
+27
-12
@@ -11,33 +11,53 @@ import {createContext} from 'react';
11
12
export type ShowFn = ({|data: Object, pageX: number, pageY: number|}) => void;
13
export type HideFn = () => void;
14
+export type OnChangeFn = boolean => void;
15
16
const idToShowFnMap = new Map<string, ShowFn>();
17
const idToHideFnMap = new Map<string, HideFn>();
18
18
-let currentHideFn = null;
19
+let currentHide: ?HideFn = null;
20
+let currentOnChange: ?OnChangeFn = null;
21
22
function hideMenu() {
21
- if (typeof currentHideFn === 'function') {
22
- currentHideFn();
23
+ if (typeof currentHide === 'function') {
24
+ currentHide();
25
+
26
+ if (typeof currentOnChange === 'function') {
27
+ currentOnChange(false);
28
+ }
29
}
30
+
31
+ currentHide = null;
32
+ currentOnChange = null;
33
}
34
35
function showMenu({
36
data,
37
id,
38
+ onChange,
39
pageX,
40
pageY,
41
}: {|
42
data: Object,
43
id: string,
44
+ onChange?: OnChangeFn,
45
pageX: number,
46
pageY: number,
47
|}) {
48
const showFn = idToShowFnMap.get(id);
49
if (typeof showFn === 'function') {
39
- currentHideFn = idToHideFnMap.get(id);
50
+ // Prevent open menus from being left hanging.
51
+ hideMenu();
52
+
53
+ currentHide = idToHideFnMap.get(id);
54
+
55
showFn({data, pageX, pageY});
56
+
57
+ if (typeof onChange === 'function') {
58
+ currentOnChange = onChange;
59
+ onChange(true);
60
+ }
61
}
62
}
63
@@ -56,14 +76,9 @@ function registerMenu(id: string, showFn: ShowFn, hideFn: HideFn) {
76
}
77
78
export type RegistryContextType = {|
59
- hideMenu: () => void,
60
- showMenu: ({|
61
- data: Object,
62
- id: string,
63
- pageX: number,
64
- pageY: number,
65
- |}) => void,
66
- registerMenu: (string, ShowFn, HideFn) => Function,
79
+ hideMenu: typeof hideMenu,
80
+ showMenu: typeof showMenu,
81
+ registerMenu: typeof registerMenu,
82
|};
83
84
export const RegistryContext = createContext<RegistryContextType>({
packages/react-devtools-shared/src/devtools/ContextMenu/useContextMenu.js
+5
-3
@@ -10,17 +10,19 @@
10
import {useContext, useEffect} from 'react';
11
import {RegistryContext} from './Contexts';
12
13
-import type {RegistryContextType} from './Contexts';
13
+import type {OnChangeFn, RegistryContextType} from './Contexts';
14
import type {ElementRef} from 'react';
15
16
export default function useContextMenu({
17
data,
18
id,
19
+ onChange,
20
ref,
21
}: {|
22
data: Object,
23
id: string,
23
- ref: {current: ElementRef<'div'> | null},
24
+ onChange?: OnChangeFn,
25
+ ref: {current: ElementRef<*> | null},
26
|}) {
27
const {showMenu} = useContext<RegistryContextType>(RegistryContext);
28
@@ -37,7 +39,7 @@ export default function useContextMenu({
39
(event: any).pageY ||
40
(event.touches && (event: any).touches[0].pageY);
41
40
- showMenu({data, id, pageX, pageY});
42
+ showMenu({data, id, onChange, pageX, pageY});
43
};
44
45
const trigger = ref.current;
packages/react-devtools-shared/src/devtools/store.js
+10
@@ -62,6 +62,7 @@ type Config = {|
62
isProfiling?: boolean,
63
supportsNativeInspection?: boolean,
64
supportsReloadAndProfile?: boolean,
65
+ supportsSchedulingProfiler?: boolean,
66
supportsProfiling?: boolean,
67
supportsTraceUpdates?: boolean,
68
|};
@@ -159,6 +160,7 @@ export default class Store extends EventEmitter<{|
160
_supportsNativeInspection: boolean = true;
161
_supportsProfiling: boolean = false;
162
_supportsReloadAndProfile: boolean = false;
163
+ _supportsSchedulingProfiler: boolean = false;
164
_supportsTraceUpdates: boolean = false;
165
166
_unsupportedBridgeProtocol: BridgeProtocol | null = null;
@@ -193,6 +195,7 @@ export default class Store extends EventEmitter<{|
195
supportsNativeInspection,
196
supportsProfiling,
197
supportsReloadAndProfile,
198
+ supportsSchedulingProfiler,
199
supportsTraceUpdates,
200
} = config;
201
this._supportsNativeInspection = supportsNativeInspection !== false;
@@ -202,6 +205,9 @@ export default class Store extends EventEmitter<{|
205
if (supportsReloadAndProfile) {
206
this._supportsReloadAndProfile = true;
207
}
208
+ if (supportsSchedulingProfiler) {
209
+ this._supportsSchedulingProfiler = true;
210
+ }
211
if (supportsTraceUpdates) {
212
this._supportsTraceUpdates = true;
213
}
@@ -414,6 +420,10 @@ export default class Store extends EventEmitter<{|
420
);
421
}
422
423
+ get supportsSchedulingProfiler(): boolean {
424
+ return this._supportsSchedulingProfiler;
425
+ }
426
+
427
get supportsTraceUpdates(): boolean {
428
return this._supportsTraceUpdates;
429
}
packages/react-devtools-shared/src/devtools/views/Components/InspectedElementHooksTree.js
+14
-10
@@ -23,6 +23,7 @@ import useContextMenu from '../../ContextMenu/useContextMenu';
23
import {meta} from '../../../hydration';
24
import {getHookSourceLocationKey} from 'react-devtools-shared/src/hookNamesCache';
25
import {enableProfilerChangedHookIndices} from 'react-devtools-feature-flags';
26
+import HookNamesContext from 'react-devtools-shared/src/devtools/views/Components/HookNamesContext';
27
28
import type {InspectedElement} from './types';
29
import type {HooksNode, HooksTree} from 'react-debug-tools/src/ReactDebugHooks';
@@ -52,6 +53,8 @@ export function InspectedElementHooksTree({
53
}: HooksTreeViewProps) {
54
const {hooks, id} = inspectedElement;
55
56
+ const {loadHookNames: loadHookNamesFunction} = useContext(HookNamesContext);
57
+
58
// Changing parseHookNames is done in a transition, because it suspends.
59
// This value is done outside of the transition, so the UI toggle feels responsive.
60
const [parseHookNamesOptimistic, setParseHookNamesOptimistic] = useState(
@@ -82,16 +85,17 @@ export function InspectedElementHooksTree({
85
<div className={styles.HooksTreeView}>
86
<div className={styles.HeaderRow}>
87
<div className={styles.Header}>hooks</div>
85
- {(!parseHookNames || hookParsingFailed) && (
86
- <Toggle
87
- className={hookParsingFailed ? styles.ToggleError : null}
88
- isChecked={parseHookNamesOptimistic}
89
- isDisabled={parseHookNamesOptimistic || hookParsingFailed}
90
- onChange={handleChange}
91
- title={toggleTitle}>
92
- <ButtonIcon type="parse-hook-names" />
93
- </Toggle>
94
- )}
88
+ {loadHookNamesFunction !== null &&
89
+ (!parseHookNames || hookParsingFailed) && (
90
+ <Toggle
91
+ className={hookParsingFailed ? styles.ToggleError : null}
92
+ isChecked={parseHookNamesOptimistic}
93
+ isDisabled={parseHookNamesOptimistic || hookParsingFailed}
94
+ onChange={handleChange}
95
+ title={toggleTitle}>
96
+ <ButtonIcon type="parse-hook-names" />
97
+ </Toggle>
98
+ )}
99
<Button onClick={handleCopy} title="Copy to clipboard">
100
<ButtonIcon type="copy" />
101
</Button>
packages/react-devtools-shared/src/devtools/views/DevTools.js
+32
-27
@@ -24,6 +24,7 @@ import {TreeContextController} from './Components/TreeContext';
24
import ViewElementSourceContext from './Components/ViewElementSourceContext';
25
import HookNamesContext from './Components/HookNamesContext';
26
import {ProfilerContextController} from './Profiler/ProfilerContext';
27
+import {SchedulingProfilerContextController} from 'react-devtools-scheduling-profiler/src/SchedulingProfilerContext';
28
import {ModalDialogContextController} from './ModalDialog';
29
import ReactLogo from './ReactLogo';
30
import UnsupportedBridgeProtocolDialog from './UnsupportedBridgeProtocolDialog';
@@ -218,36 +219,40 @@ export default function DevTools({
219
<HookNamesContext.Provider value={hookNamesContext}>
220
<TreeContextController>
221
<ProfilerContextController>
221
- <div className={styles.DevTools} ref={devToolsRef}>
222
- {showTabBar && (
223
- <div className={styles.TabBar}>
224
- <ReactLogo />
225
- <span className={styles.DevToolsVersion}>
226
- {process.env.DEVTOOLS_VERSION}
227
- </span>
228
- <div className={styles.Spacer} />
229
- <TabBar
230
- currentTab={tab}
231
- id="DevTools"
232
- selectTab={setTab}
233
- tabs={tabs}
234
- type="navigation"
222
+ <SchedulingProfilerContextController>
223
+ <div className={styles.DevTools} ref={devToolsRef}>
224
+ {showTabBar && (
225
+ <div className={styles.TabBar}>
226
+ <ReactLogo />
227
+ <span className={styles.DevToolsVersion}>
228
+ {process.env.DEVTOOLS_VERSION}
229
+ </span>
230
+ <div className={styles.Spacer} />
231
+ <TabBar
232
+ currentTab={tab}
233
+ id="DevTools"
234
+ selectTab={setTab}
235
+ tabs={tabs}
236
+ type="navigation"
237
+ />
238
+ </div>
239
+ )}
240
+ <div
241
+ className={styles.TabContent}
242
+ hidden={tab !== 'components'}>
243
+ <Components
244
+ portalContainer={componentsPortalContainer}
245
+ />
246
+ </div>
247
+ <div
248
+ className={styles.TabContent}
249
+ hidden={tab !== 'profiler'}>
250
+ <Profiler
251
+ portalContainer={profilerPortalContainer}
252
/>
253
</div>
237
- )}
238
- <div
239
- className={styles.TabContent}
240
- hidden={tab !== 'components'}>
241
- <Components
242
- portalContainer={componentsPortalContainer}
243
- />
244
- </div>
245
- <div
246
- className={styles.TabContent}
247
- hidden={tab !== 'profiler'}>
248
- <Profiler portalContainer={profilerPortalContainer} />
254
</div>
250
- </div>
255
+ </SchedulingProfilerContextController>
256
</ProfilerContextController>
257
</TreeContextController>
258
</HookNamesContext.Provider>
packages/react-devtools-shared/src/devtools/views/Icon.js
+9
@@ -21,6 +21,7 @@ export type IconType =
21
| 'flame-chart'
22
| 'profiler'
23
| 'ranked-chart'
24
+ | 'scheduling-profiler'
25
| 'search'
26
| 'settings'
27
| 'store-as-global-variable'
@@ -64,6 +65,9 @@ export default function Icon({className = '', type}: Props) {
65
case 'ranked-chart':
66
pathData = PATH_RANKED_CHART;
67
break;
68
+ case 'scheduling-profiler':
69
+ pathData = PATH_SCHEDULING_PROFILER;
70
+ break;
71
case 'search':
72
pathData = PATH_SEARCH;
73
break;
@@ -136,6 +140,11 @@ const PATH_FLAME_CHART = `
140
141
const PATH_PROFILER = 'M5 9.2h3V19H5zM10.6 5h2.8v14h-2.8zm5.6 8H19v6h-2.8z';
142
143
+const PATH_SCHEDULING_PROFILER = `
144
+ M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0
145
+ 16H5V9h14v10zm0-12H5V5h14v2zM7 11h5v5H7z
146
+`;
147
+
148
const PATH_SEARCH = `
149
M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91
150
16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99
packages/react-devtools-shared/src/devtools/views/Profiler/Profiler.js
+26
-3
@@ -16,6 +16,7 @@ import ClearProfilingDataButton from './ClearProfilingDataButton';
16
import CommitFlamegraph from './CommitFlamegraph';
17
import CommitRanked from './CommitRanked';
18
import RootSelector from './RootSelector';
19
+import {SchedulingProfiler} from 'react-devtools-scheduling-profiler/src/SchedulingProfiler';
20
import RecordToggle from './RecordToggle';
21
import ReloadAndProfileButton from './ReloadAndProfileButton';
22
import ProfilingImportExportButtons from './ProfilingImportExportButtons';
@@ -26,6 +27,7 @@ import SettingsModal from 'react-devtools-shared/src/devtools/views/Settings/Set
27
import SettingsModalContextToggle from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContextToggle';
28
import {SettingsModalContextController} from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContext';
29
import portaledContent from '../portaledContent';
30
+import {StoreContext} from '../context';
31
32
import styles from './Profiler.css';
33
@@ -41,8 +43,12 @@ function Profiler(_: {||}) {
43
supportsProfiling,
44
} = useContext(ProfilerContext);
45
46
+ const {supportsSchedulingProfiler} = useContext(StoreContext);
47
+
48
+ let showRightColumn = true;
49
+
50
let view = null;
45
- if (didRecordCommits) {
51
+ if (didRecordCommits || selectedTabID === 'scheduling-profiler') {
52
switch (selectedTabID) {
53
case 'flame-chart':
54
view = <CommitFlamegraph />;
@@ -50,6 +56,10 @@ function Profiler(_: {||}) {
56
case 'ranked-chart':
57
view = <CommitRanked />;
58
break;
59
+ case 'scheduling-profiler':
60
+ view = <SchedulingProfiler />;
61
+ showRightColumn = false;
62
+ break;
63
default:
64
break;
65
}
@@ -101,7 +111,9 @@ function Profiler(_: {||}) {
111
currentTab={selectedTabID}
112
id="Profiler"
113
selectTab={selectTab}
104
- tabs={tabs}
114
+ tabs={
115
+ supportsSchedulingProfiler ? tabsWithSchedulingProfiler : tabs
116
+ }
117
type="profiler"
118
/>
119
<RootSelector />
@@ -119,7 +131,7 @@ function Profiler(_: {||}) {
131
<ModalDialog />
132
</div>
133
</div>
122
- <div className={styles.RightColumn}>{sidebar}</div>
134
+ {showRightColumn && <div className={styles.RightColumn}>{sidebar}</div>}
135
<SettingsModal />
136
</div>
137
</SettingsModalContextController>
@@ -141,6 +153,17 @@ const tabs = [
153
},
154
];
155
156
+const tabsWithSchedulingProfiler = [
157
+ ...tabs,
158
+ null, // Divider/separator
159
+ {
160
+ id: 'scheduling-profiler',
161
+ icon: 'scheduling-profiler',
162
+ label: 'Scheduling',
163
+ title: 'Scheduling Profiler',
164
+ },
165
+];
166
+
167
const NoProfilingData = () => (
168
<div className={styles.Column}>
169
<div className={styles.Header}>No profiling data has been recorded.</div>
packages/react-devtools-shared/src/devtools/views/Profiler/ProfilerContext.js
+2
-1
@@ -19,7 +19,8 @@ import {StoreContext} from '../context';
19
20
import type {ProfilingDataFrontend} from './types';
21
22
-export type TabID = 'flame-chart' | 'ranked-chart';
22
+// TODO (scheduling profiler) Should this be its own context?
23
+export type TabID = 'flame-chart' | 'ranked-chart' | 'scheduling-profiler';
24
25
export type Context = {|
26
// Which tab is selected in the Profiler UI?
packages/react-devtools-shared/src/devtools/views/Profiler/ProfilingImportExportButtons.js
+25
-6
@@ -19,13 +19,17 @@ import {
19
prepareProfilingDataFrontendFromExport,
20
} from './utils';
21
import {downloadFile} from '../utils';
22
+import {SchedulingProfilerContext} from 'react-devtools-scheduling-profiler/src/SchedulingProfilerContext';
23
24
import styles from './ProfilingImportExportButtons.css';
25
26
import type {ProfilingDataExport} from './types';
27
28
export default function ProfilingImportExportButtons() {
28
- const {isProfiling, profilingData, rootID} = useContext(ProfilerContext);
29
+ const {isProfiling, profilingData, rootID, selectedTabID} = useContext(
30
+ ProfilerContext,
31
+ );
32
+ const {importSchedulingProfilerData} = useContext(SchedulingProfilerContext);
33
const store = useContext(StoreContext);
34
const {profilerStore} = store;
35
@@ -64,13 +68,13 @@ export default function ProfilingImportExportButtons() {
68
}
69
}, [rootID, profilingData]);
70
67
- const uploadData = useCallback(() => {
71
+ const clickInputElement = useCallback(() => {
72
if (inputRef.current !== null) {
73
inputRef.current.click();
74
}
75
}, []);
76
73
- const handleFiles = useCallback(() => {
77
+ const importProfilerData = useCallback(() => {
78
const input = inputRef.current;
79
if (input !== null && input.files.length > 0) {
80
const fileReader = new FileReader();
@@ -104,6 +108,13 @@ export default function ProfilingImportExportButtons() {
108
}
109
}, [modalDialogDispatch, profilerStore]);
110
111
+ const importSchedulingProfilerDataWrapper = event => {
112
+ const input = inputRef.current;
113
+ if (input !== null && input.files.length > 0) {
114
+ importSchedulingProfilerData(input.files[0]);
115
+ }
116
+ };
117
+
118
return (
119
<Fragment>
120
<div className={styles.VRule} />
@@ -111,18 +122,26 @@ export default function ProfilingImportExportButtons() {
122
ref={inputRef}
123
className={styles.Input}
124
type="file"
114
- onChange={handleFiles}
125
+ onChange={
126
+ selectedTabID === 'scheduling-profiler'
127
+ ? importSchedulingProfilerDataWrapper
128
+ : importProfilerData
129
+ }
130
tabIndex={-1}
131
/>
132
<a ref={downloadRef} className={styles.Input} />
133
<Button
134
disabled={isProfiling}
120
- onClick={uploadData}
135
+ onClick={clickInputElement}
136
title="Load profile...">
137
<ButtonIcon type="import" />
138
</Button>
139
<Button
125
- disabled={isProfiling || !profilerStore.didRecordCommits}
140
+ disabled={
141
+ isProfiling ||
142
+ !profilerStore.didRecordCommits ||
143
+ selectedTabID === 'scheduling-profiler'
144
+ }
145
onClick={downloadData}
146
title="Save profile...">
147
<ButtonIcon type="export" />
packages/react-devtools-shared/src/devtools/views/Settings/SettingsContext.js
+137
-1
@@ -411,11 +411,16 @@ export function updateThemeVariables(
411
updateStyleHelper(theme, 'color-record-active', documentElements);
412
updateStyleHelper(theme, 'color-record-hover', documentElements);
413
updateStyleHelper(theme, 'color-record-inactive', documentElements);
414
+ updateStyleHelper(theme, 'color-resize-bar', documentElements);
415
updateStyleHelper(theme, 'color-color-scroll-thumb', documentElements);
416
updateStyleHelper(theme, 'color-color-scroll-track', documentElements);
417
updateStyleHelper(theme, 'color-search-match', documentElements);
417
- updateStyleHelper(theme, 'color-shadow', documentElements);
418
updateStyleHelper(theme, 'color-search-match-current', documentElements);
419
+ updateStyleHelper(
420
+ theme,
421
+ 'color-scheduling-profiler-flame-graph-label',
422
+ documentElements,
423
+ );
424
updateStyleHelper(
425
theme,
426
'color-selected-tree-highlight-active',
@@ -426,6 +431,137 @@ export function updateThemeVariables(
431
'color-selected-tree-highlight-inactive',
432
documentElements,
433
);
434
+ updateStyleHelper(
435
+ theme,
436
+ 'color-scheduling-profiler-priority-background',
437
+ documentElements,
438
+ );
439
+ updateStyleHelper(
440
+ theme,
441
+ 'color-scheduling-profiler-priority-border',
442
+ documentElements,
443
+ );
444
+ updateStyleHelper(
445
+ theme,
446
+ 'color-scheduling-profiler-user-timing',
447
+ documentElements,
448
+ );
449
+ updateStyleHelper(
450
+ theme,
451
+ 'color-scheduling-profiler-user-timing-hover',
452
+ documentElements,
453
+ );
454
+ updateStyleHelper(
455
+ theme,
456
+ 'color-scheduling-profiler-react-idle',
457
+ documentElements,
458
+ );
459
+ updateStyleHelper(
460
+ theme,
461
+ 'color-scheduling-profiler-react-idle-selected',
462
+ documentElements,
463
+ );
464
+ updateStyleHelper(
465
+ theme,
466
+ 'color-scheduling-profiler-react-idle-hover',
467
+ documentElements,
468
+ );
469
+ updateStyleHelper(
470
+ theme,
471
+ 'color-scheduling-profiler-react-render',
472
+ documentElements,
473
+ );
474
+ updateStyleHelper(
475
+ theme,
476
+ 'color-scheduling-profiler-react-render-selected',
477
+ documentElements,
478
+ );
479
+ updateStyleHelper(
480
+ theme,
481
+ 'color-scheduling-profiler-react-render-hover',
482
+ documentElements,
483
+ );
484
+ updateStyleHelper(
485
+ theme,
486
+ 'color-scheduling-profiler-react-commit',
487
+ documentElements,
488
+ );
489
+ updateStyleHelper(
490
+ theme,
491
+ 'color-scheduling-profiler-react-commit-selected',
492
+ documentElements,
493
+ );
494
+ updateStyleHelper(
495
+ theme,
496
+ 'color-scheduling-profiler-react-commit-hover',
497
+ documentElements,
498
+ );
499
+ updateStyleHelper(
500
+ theme,
501
+ 'color-scheduling-profiler-react-layout-effects',
502
+ documentElements,
503
+ );
504
+ updateStyleHelper(
505
+ theme,
506
+ 'color-scheduling-profiler-react-layout-effects-selected',
507
+ documentElements,
508
+ );
509
+ updateStyleHelper(
510
+ theme,
511
+ 'color-scheduling-profiler-react-layout-effects-hover',
512
+ documentElements,
513
+ );
514
+ updateStyleHelper(
515
+ theme,
516
+ 'color-scheduling-profiler-react-passive-effects',
517
+ documentElements,
518
+ );
519
+ updateStyleHelper(
520
+ theme,
521
+ 'color-scheduling-profiler-react-passive-effects-selected',
522
+ documentElements,
523
+ );
524
+ updateStyleHelper(
525
+ theme,
526
+ 'color-scheduling-profiler-react-passive-effects-hover',
527
+ documentElements,
528
+ );
529
+ updateStyleHelper(
530
+ theme,
531
+ 'color-scheduling-profiler-react-schedule',
532
+ documentElements,
533
+ );
534
+ updateStyleHelper(
535
+ theme,
536
+ 'color-scheduling-profiler-react-schedule-hover',
537
+ documentElements,
538
+ );
539
+ updateStyleHelper(
540
+ theme,
541
+ 'color-scheduling-profiler-react-schedule-cascading',
542
+ documentElements,
543
+ );
544
+ updateStyleHelper(
545
+ theme,
546
+ 'color-scheduling-profiler-react-schedule-cascading-hover',
547
+ documentElements,
548
+ );
549
+ updateStyleHelper(
550
+ theme,
551
+ 'color-scheduling-profiler-react-suspend',
552
+ documentElements,
553
+ );
554
+ updateStyleHelper(
555
+ theme,
556
+ 'color-scheduling-profiler-react-suspend-hover',
557
+ documentElements,
558
+ );
559
+ updateStyleHelper(
560
+ theme,
561
+ 'color-scheduling-profiler-react-work-border',
562
+ documentElements,
563
+ );
564
+ updateStyleHelper(theme, 'color-shadow', documentElements);
565
updateStyleHelper(theme, 'color-tab-selected-border', documentElements);
566
updateStyleHelper(theme, 'color-text', documentElements);
567
updateStyleHelper(theme, 'color-text-invalid', documentElements);
packages/react-devtools-shared/src/devtools/views/TabBar.css
+8
@@ -85,6 +85,14 @@
85
.TabLabelSettings {
86
}
87
88
+.VRule {
89
+ height: 20px;
90
+ width: 1px;
91
+ border-left: 1px solid var(--color-border);
92
+ padding-left: 0.25rem;
93
+ margin-left: 0.25rem;
94
+}
95
+
96
@media screen and (max-width: 525px) {
97
.IconSizeNavigation {
98
margin-right: 0;
packages/react-devtools-shared/src/devtools/views/TabBar.js
+11
-4
@@ -29,7 +29,7 @@ export type Props = {|
29
disabled?: boolean,
30
id: string,
31
selectTab: (tabID: any) => void,
32
- tabs: Array<TabInfo>,
32
+ tabs: Array<TabInfo | null>,
33
type: 'navigation' | 'profiler' | 'settings',
34
|};
35
@@ -41,8 +41,9 @@ export default function TabBar({
41
tabs,
42
type,
43
}: Props) {
44
- if (!tabs.some(tab => tab.id === currentTab)) {
45
- selectTab(tabs[0].id);
44
+ if (!tabs.some(tab => tab !== null && tab.id === currentTab)) {
45
+ const firstTab = ((tabs.find(tab => tab !== null): any): TabInfo);
46
+ selectTab(firstTab.id);
47
}
48
49
const onChange = useCallback(
@@ -88,7 +89,13 @@ export default function TabBar({
89
90
return (
91
<Fragment>
91
- {tabs.map(({icon, id, label, title}) => {
92
+ {tabs.map(tab => {
93
+ if (tab === null) {
94
+ return <div className={styles.VRule} />;
95
+ }
96
+
97
+ const {icon, id, label, title} = tab;
98
+
99
let button = (
100
<label
101
className={[
packages/react-devtools-shared/src/devtools/views/Tooltip.css
+1
@@ -6,6 +6,7 @@
6
font-size: 12px;
7
background-color: var(--color-tooltip-background);
8
color: var(--color-tooltip-text);
9
+ box-shadow: 1px 1px 2px var(--color-shadow);
10
11
/* Make sure this is above the DevTools, which are above the Overlay */
12
z-index: 10000002;
packages/react-devtools-shared/src/devtools/views/root.css
+58
-2
@@ -77,6 +77,34 @@
77
--light-color-record-active: #fc3a4b;
78
--light-color-record-hover: #3578e5;
79
--light-color-record-inactive: #0088fa;
80
+ --light-color-resize-bar: #cccccc;
81
+ --light-color-scheduling-profiler-flame-graph-label: #000000;
82
+ --light-color-scheduling-profiler-priority-background: #ededf0;
83
+ --light-color-scheduling-profiler-priority-border: #d7d7db;
84
+ --light-color-scheduling-profiler-user-timing: #c9cacd;
85
+ --light-color-scheduling-profiler-user-timing-hover:#93959a;
86
+ --light-color-scheduling-profiler-react-idle: #edf6ff;
87
+ --light-color-scheduling-profiler-react-idle-selected:#EDF6FF;
88
+ --light-color-scheduling-profiler-react-idle-hover:#EDF6FF;
89
+ --light-color-scheduling-profiler-react-render: #9fc3f3;
90
+ --light-color-scheduling-profiler-react-render-selected:#64A9F5;
91
+ --light-color-scheduling-profiler-react-render-hover:#2683E2;
92
+ --light-color-scheduling-profiler-react-commit: #ff718e;
93
+ --light-color-scheduling-profiler-react-commit-selected:#FF5277;
94
+ --light-color-scheduling-profiler-react-commit-hover:#ed0030;
95
+ --light-color-scheduling-profiler-react-layout-effects:#c88ff0;
96
+ --light-color-scheduling-profiler-react-layout-effects-selected:#934FC1;
97
+ --light-color-scheduling-profiler-react-layout-effects-hover:#601593;
98
+ --light-color-scheduling-profiler-react-passive-effects:#c88ff0;
99
+ --light-color-scheduling-profiler-react-passive-effects-selected:#934FC1;
100
+ --light-color-scheduling-profiler-react-passive-effects-hover:#601593;
101
+ --light-color-scheduling-profiler-react-schedule: #9fc3f3;
102
+ --light-color-scheduling-profiler-react-schedule-hover:#2683E2;
103
+ --light-color-scheduling-profiler-react-schedule-cascading:#ff718e;
104
+ --light-color-scheduling-profiler-react-schedule-cascading-hover:#ed0030;
105
+ --light-color-scheduling-profiler-react-suspend: #a6e59f;
106
+ --light-color-scheduling-profiler-react-suspend-hover:#13bc00;
107
+ --light-color-scheduling-profiler-react-work-border:#ffffff;
108
--light-color-scroll-thumb: #c2c2c2;
109
--light-color-scroll-track: #fafafa;
110
--light-color-search-match: yellow;
@@ -146,7 +174,7 @@
174
--dark-color-console-warning-border: #665500;
175
--dark-color-console-warning-icon: #f4bd00;
176
--dark-color-console-warning-text: #f5f2ed;
149
- --dark-color-context-background: rgba(255,255,255,.9);
177
+ --dark-color-context-background: rgba(255,255,255,.95);
178
--dark-color-context-background-hover: rgba(0, 136, 250, 0.1);
179
--dark-color-context-background-selected: #0088fa;
180
--dark-color-context-border: #eeeeee;
@@ -169,6 +197,34 @@
197
--dark-color-record-active: #fc3a4b;
198
--dark-color-record-hover: #a2e9fc;
199
--dark-color-record-inactive: #61dafb;
200
+ --dark-color-resize-bar: #3d424a;
201
+ --dark-color-scheduling-profiler-flame-graph-label: #000000;
202
+ --dark-color-scheduling-profiler-priority-background: #1d2129;
203
+ --dark-color-scheduling-profiler-priority-border: #282c34;
204
+ --dark-color-scheduling-profiler-user-timing: #c9cacd;
205
+ --dark-color-scheduling-profiler-user-timing-hover:#93959a;
206
+ --dark-color-scheduling-profiler-react-idle: #3d485b;
207
+ --dark-color-scheduling-profiler-react-idle-selected:#465269;
208
+ --dark-color-scheduling-profiler-react-idle-hover:#465269;
209
+ --dark-color-scheduling-profiler-react-render: #9fc3f3;
210
+ --dark-color-scheduling-profiler-react-render-selected:#64A9F5;
211
+ --dark-color-scheduling-profiler-react-render-hover:#2683E2;
212
+ --dark-color-scheduling-profiler-react-commit: #ff718e;
213
+ --dark-color-scheduling-profiler-react-commit-selected:#FF5277;
214
+ --dark-color-scheduling-profiler-react-commit-hover:#ed0030;
215
+ --dark-color-scheduling-profiler-react-layout-effects:#c88ff0;
216
+ --dark-color-scheduling-profiler-react-layout-effects-selected:#934FC1;
217
+ --dark-color-scheduling-profiler-react-layout-effects-hover:#601593;
218
+ --dark-color-scheduling-profiler-react-passive-effects:#c88ff0;
219
+ --dark-color-scheduling-profiler-react-passive-effects-selected:#934FC1;
220
+ --dark-color-scheduling-profiler-react-passive-effects-hover:#601593;
221
+ --dark-color-scheduling-profiler-react-schedule: #9fc3f3;
222
+ --dark-color-scheduling-profiler-react-schedule-hover:#2683E2;
223
+ --dark-color-scheduling-profiler-react-schedule-cascading:#ff718e;
224
+ --dark-color-scheduling-profiler-react-schedule-cascading-hover:#ed0030;
225
+ --dark-color-scheduling-profiler-react-suspend: #a6e59f;
226
+ --dark-color-scheduling-profiler-react-suspend-hover:#13bc00;
227
+ --dark-color-scheduling-profiler-react-work-border:#ffffff;
228
--dark-color-scroll-thumb: #afb3b9;
229
--dark-color-scroll-track: #313640;
230
--dark-color-search-match: yellow;
@@ -184,7 +240,7 @@
240
--dark-color-toggle-background-on: #178fb9;
241
--dark-color-toggle-background-off: #777d88;
242
--dark-color-toggle-text: #ffffff;
187
- --dark-color-tooltip-background: rgba(255, 255, 255, 0.9);
243
+ --dark-color-tooltip-background: rgba(255, 255, 255, 0.95);
244
--dark-color-tooltip-text: #000000;
245
246
/* Font smoothing */
packages/react-devtools-shared/src/hookNamesCache.js
+8
-1
@@ -103,7 +103,14 @@ export function loadHookNames(
103
104
let didTimeout = false;
105
106
- loadHookNamesFunction(hooksTree).then(
106
+ const response = loadHookNamesFunction(hooksTree);
107
+ console.log(
108
+ 'loadHookNamesFunction:',
109
+ loadHookNamesFunction,
110
+ '->',
111
+ response,
112
+ );
113
+ response.then(
114
function onSuccess(hookNames) {
115
if (didTimeout) {
116
return;
yarn.lock
+45
@@ -2236,6 +2236,11 @@
2236
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd"
2237
integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==
2238
2239
+"@types/json-schema@^7.0.8":
2240
+ version "7.0.8"
2241
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818"
2242
+ integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==
2243
+
2244
"@types/minimatch@*", "@types/minimatch@^3.0.3":
2245
version "3.0.3"
2246
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
@@ -2841,6 +2846,11 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
2846
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da"
2847
integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==
2848
2849
+ajv-keywords@^3.5.2:
2850
+ version "3.5.2"
2851
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
2852
+ integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
2853
+
2854
ajv-merge-patch@4.1.0:
2855
version "4.1.0"
2856
resolved "https://registry.yarnpkg.com/ajv-merge-patch/-/ajv-merge-patch-4.1.0.tgz#cd580e5860ac53431d6aa901fa3d5e2eb2b74a6c"
@@ -2869,6 +2879,16 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2:
2879
json-schema-traverse "^0.4.1"
2880
uri-js "^4.2.2"
2881
2882
+ajv@^6.12.5:
2883
+ version "6.12.6"
2884
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
2885
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
2886
+ dependencies:
2887
+ fast-deep-equal "^3.1.1"
2888
+ fast-json-stable-stringify "^2.0.0"
2889
+ json-schema-traverse "^0.4.1"
2890
+ uri-js "^4.2.2"
2891
+
2892
ajv@^6.5.5, ajv@^6.9.1:
2893
version "6.10.2"
2894
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52"
@@ -6768,6 +6788,14 @@ file-loader@^6.0.0:
6788
loader-utils "^2.0.0"
6789
schema-utils "^2.6.5"
6790
6791
+file-loader@^6.1.0:
6792
+ version "6.2.0"
6793
+ resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d"
6794
+ integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
6795
+ dependencies:
6796
+ loader-utils "^2.0.0"
6797
+ schema-utils "^3.0.0"
6798
+
6799
file-uri-to-path@1.0.0:
6800
version "1.0.0"
6801
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
@@ -12631,6 +12659,15 @@ schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0:
12659
ajv "^6.12.2"
12660
ajv-keywords "^3.4.1"
12661
12662
+schema-utils@^3.0.0:
12663
+ version "3.1.1"
12664
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
12665
+ integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
12666
+ dependencies:
12667
+ "@types/json-schema" "^7.0.8"
12668
+ ajv "^6.12.5"
12669
+ ajv-keywords "^3.5.2"
12670
+
12671
select-hose@^2.0.0:
12672
version "2.0.0"
12673
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@@ -14877,6 +14914,14 @@ worker-loader@^3.0.2:
14914
loader-utils "^2.0.0"
14915
schema-utils "^2.7.0"
14916
14917
+worker-loader@^3.0.3:
14918
+ version "3.0.8"
14919
+ resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-3.0.8.tgz#5fc5cda4a3d3163d9c274a4e3a811ce8b60dbb37"
14920
+ integrity sha512-XQyQkIFeRVC7f7uRhFdNMe/iJOdO6zxAaR3EWbDp45v3mDhrTi+++oswKNxShUNjPC/1xUp5DB29YKLhFo129g==
14921
+ dependencies:
14922
+ loader-utils "^2.0.0"
14923
+ schema-utils "^3.0.0"
14924
+
14925
workerize-loader@^1.3.0:
14926
version "1.3.0"
14927
resolved "https://registry.yarnpkg.com/workerize-loader/-/workerize-loader-1.3.0.tgz#4995cf2ff2b45dd6dc60e4411e63f5ae2c704d36"