[DRAFT] Import scheduling profiler into DevTools Profiler (#21897)

This commit is contained in:
Brian Vaughn
2021-07-22 13:58:57 -04:00
committed by GitHub
parent ae5d26154b
commit f4161c3ec7
63 changed files with 935 additions and 1132 deletions
@@ -1,7 +1,9 @@
.ContextMenu {
position: absolute;
background-color: var(--color-context-background);
box-shadow: 1px 1px 2px var(--color-shadow);
border-radius: 0.25rem;
overflow: hidden;
z-index: 10000002;
user-select: none;
}
@@ -54,7 +54,9 @@ type Props = {|
|};
export default function ContextMenu({children, id}: Props) {
const {registerMenu} = useContext<RegistryContextType>(RegistryContext);
const {hideMenu, registerMenu} = useContext<RegistryContextType>(
RegistryContext,
);
const [state, setState] = useState(HIDDEN_STATE);
@@ -75,11 +77,11 @@ export default function ContextMenu({children, id}: Props) {
}, []);
useEffect(() => {
const showMenu = ({data, pageX, pageY}) => {
const showMenuFn = ({data, pageX, pageY}) => {
setState({data, isVisible: true, pageX, pageY});
};
const hideMenu = () => setState(HIDDEN_STATE);
return registerMenu(id, showMenu, hideMenu);
const hideMenuFn = () => setState(HIDDEN_STATE);
return registerMenu(id, showMenuFn, hideMenuFn);
}, [id]);
useLayoutEffect(() => {
@@ -92,21 +94,17 @@ export default function ContextMenu({children, id}: Props) {
if (container !== null) {
const hideUnlessContains = event => {
if (!menu.contains(event.target)) {
setState(HIDDEN_STATE);
hideMenu();
}
};
const hide = event => {
setState(HIDDEN_STATE);
};
const ownerDocument = container.ownerDocument;
ownerDocument.addEventListener('mousedown', hideUnlessContains);
ownerDocument.addEventListener('touchstart', hideUnlessContains);
ownerDocument.addEventListener('keydown', hideUnlessContains);
const ownerWindow = ownerDocument.defaultView;
ownerWindow.addEventListener('resize', hide);
ownerWindow.addEventListener('resize', hideMenu);
repositionToFit(menu, state.pageX, state.pageY);
@@ -115,7 +113,7 @@ export default function ContextMenu({children, id}: Props) {
ownerDocument.removeEventListener('touchstart', hideUnlessContains);
ownerDocument.removeEventListener('keydown', hideUnlessContains);
ownerWindow.removeEventListener('resize', hide);
ownerWindow.removeEventListener('resize', hideMenu);
};
}
}, [state]);
@@ -11,33 +11,53 @@ import {createContext} from 'react';
export type ShowFn = ({|data: Object, pageX: number, pageY: number|}) => void;
export type HideFn = () => void;
export type OnChangeFn = boolean => void;
const idToShowFnMap = new Map<string, ShowFn>();
const idToHideFnMap = new Map<string, HideFn>();
let currentHideFn = null;
let currentHide: ?HideFn = null;
let currentOnChange: ?OnChangeFn = null;
function hideMenu() {
if (typeof currentHideFn === 'function') {
currentHideFn();
if (typeof currentHide === 'function') {
currentHide();
if (typeof currentOnChange === 'function') {
currentOnChange(false);
}
}
currentHide = null;
currentOnChange = null;
}
function showMenu({
data,
id,
onChange,
pageX,
pageY,
}: {|
data: Object,
id: string,
onChange?: OnChangeFn,
pageX: number,
pageY: number,
|}) {
const showFn = idToShowFnMap.get(id);
if (typeof showFn === 'function') {
currentHideFn = idToHideFnMap.get(id);
// Prevent open menus from being left hanging.
hideMenu();
currentHide = idToHideFnMap.get(id);
showFn({data, pageX, pageY});
if (typeof onChange === 'function') {
currentOnChange = onChange;
onChange(true);
}
}
}
@@ -56,14 +76,9 @@ function registerMenu(id: string, showFn: ShowFn, hideFn: HideFn) {
}
export type RegistryContextType = {|
hideMenu: () => void,
showMenu: ({|
data: Object,
id: string,
pageX: number,
pageY: number,
|}) => void,
registerMenu: (string, ShowFn, HideFn) => Function,
hideMenu: typeof hideMenu,
showMenu: typeof showMenu,
registerMenu: typeof registerMenu,
|};
export const RegistryContext = createContext<RegistryContextType>({
@@ -10,17 +10,19 @@
import {useContext, useEffect} from 'react';
import {RegistryContext} from './Contexts';
import type {RegistryContextType} from './Contexts';
import type {OnChangeFn, RegistryContextType} from './Contexts';
import type {ElementRef} from 'react';
export default function useContextMenu({
data,
id,
onChange,
ref,
}: {|
data: Object,
id: string,
ref: {current: ElementRef<'div'> | null},
onChange?: OnChangeFn,
ref: {current: ElementRef<*> | null},
|}) {
const {showMenu} = useContext<RegistryContextType>(RegistryContext);
@@ -37,7 +39,7 @@ export default function useContextMenu({
(event: any).pageY ||
(event.touches && (event: any).touches[0].pageY);
showMenu({data, id, pageX, pageY});
showMenu({data, id, onChange, pageX, pageY});
};
const trigger = ref.current;
+10
View File
@@ -62,6 +62,7 @@ type Config = {|
isProfiling?: boolean,
supportsNativeInspection?: boolean,
supportsReloadAndProfile?: boolean,
supportsSchedulingProfiler?: boolean,
supportsProfiling?: boolean,
supportsTraceUpdates?: boolean,
|};
@@ -159,6 +160,7 @@ export default class Store extends EventEmitter<{|
_supportsNativeInspection: boolean = true;
_supportsProfiling: boolean = false;
_supportsReloadAndProfile: boolean = false;
_supportsSchedulingProfiler: boolean = false;
_supportsTraceUpdates: boolean = false;
_unsupportedBridgeProtocol: BridgeProtocol | null = null;
@@ -193,6 +195,7 @@ export default class Store extends EventEmitter<{|
supportsNativeInspection,
supportsProfiling,
supportsReloadAndProfile,
supportsSchedulingProfiler,
supportsTraceUpdates,
} = config;
this._supportsNativeInspection = supportsNativeInspection !== false;
@@ -202,6 +205,9 @@ export default class Store extends EventEmitter<{|
if (supportsReloadAndProfile) {
this._supportsReloadAndProfile = true;
}
if (supportsSchedulingProfiler) {
this._supportsSchedulingProfiler = true;
}
if (supportsTraceUpdates) {
this._supportsTraceUpdates = true;
}
@@ -414,6 +420,10 @@ export default class Store extends EventEmitter<{|
);
}
get supportsSchedulingProfiler(): boolean {
return this._supportsSchedulingProfiler;
}
get supportsTraceUpdates(): boolean {
return this._supportsTraceUpdates;
}
@@ -23,6 +23,7 @@ import useContextMenu from '../../ContextMenu/useContextMenu';
import {meta} from '../../../hydration';
import {getHookSourceLocationKey} from 'react-devtools-shared/src/hookNamesCache';
import {enableProfilerChangedHookIndices} from 'react-devtools-feature-flags';
import HookNamesContext from 'react-devtools-shared/src/devtools/views/Components/HookNamesContext';
import type {InspectedElement} from './types';
import type {HooksNode, HooksTree} from 'react-debug-tools/src/ReactDebugHooks';
@@ -52,6 +53,8 @@ export function InspectedElementHooksTree({
}: HooksTreeViewProps) {
const {hooks, id} = inspectedElement;
const {loadHookNames: loadHookNamesFunction} = useContext(HookNamesContext);
// Changing parseHookNames is done in a transition, because it suspends.
// This value is done outside of the transition, so the UI toggle feels responsive.
const [parseHookNamesOptimistic, setParseHookNamesOptimistic] = useState(
@@ -82,16 +85,17 @@ export function InspectedElementHooksTree({
<div className={styles.HooksTreeView}>
<div className={styles.HeaderRow}>
<div className={styles.Header}>hooks</div>
{(!parseHookNames || hookParsingFailed) && (
<Toggle
className={hookParsingFailed ? styles.ToggleError : null}
isChecked={parseHookNamesOptimistic}
isDisabled={parseHookNamesOptimistic || hookParsingFailed}
onChange={handleChange}
title={toggleTitle}>
<ButtonIcon type="parse-hook-names" />
</Toggle>
)}
{loadHookNamesFunction !== null &&
(!parseHookNames || hookParsingFailed) && (
<Toggle
className={hookParsingFailed ? styles.ToggleError : null}
isChecked={parseHookNamesOptimistic}
isDisabled={parseHookNamesOptimistic || hookParsingFailed}
onChange={handleChange}
title={toggleTitle}>
<ButtonIcon type="parse-hook-names" />
</Toggle>
)}
<Button onClick={handleCopy} title="Copy to clipboard">
<ButtonIcon type="copy" />
</Button>
+32 -27
View File
@@ -24,6 +24,7 @@ import {TreeContextController} from './Components/TreeContext';
import ViewElementSourceContext from './Components/ViewElementSourceContext';
import HookNamesContext from './Components/HookNamesContext';
import {ProfilerContextController} from './Profiler/ProfilerContext';
import {SchedulingProfilerContextController} from 'react-devtools-scheduling-profiler/src/SchedulingProfilerContext';
import {ModalDialogContextController} from './ModalDialog';
import ReactLogo from './ReactLogo';
import UnsupportedBridgeProtocolDialog from './UnsupportedBridgeProtocolDialog';
@@ -218,36 +219,40 @@ export default function DevTools({
<HookNamesContext.Provider value={hookNamesContext}>
<TreeContextController>
<ProfilerContextController>
<div className={styles.DevTools} ref={devToolsRef}>
{showTabBar && (
<div className={styles.TabBar}>
<ReactLogo />
<span className={styles.DevToolsVersion}>
{process.env.DEVTOOLS_VERSION}
</span>
<div className={styles.Spacer} />
<TabBar
currentTab={tab}
id="DevTools"
selectTab={setTab}
tabs={tabs}
type="navigation"
<SchedulingProfilerContextController>
<div className={styles.DevTools} ref={devToolsRef}>
{showTabBar && (
<div className={styles.TabBar}>
<ReactLogo />
<span className={styles.DevToolsVersion}>
{process.env.DEVTOOLS_VERSION}
</span>
<div className={styles.Spacer} />
<TabBar
currentTab={tab}
id="DevTools"
selectTab={setTab}
tabs={tabs}
type="navigation"
/>
</div>
)}
<div
className={styles.TabContent}
hidden={tab !== 'components'}>
<Components
portalContainer={componentsPortalContainer}
/>
</div>
<div
className={styles.TabContent}
hidden={tab !== 'profiler'}>
<Profiler
portalContainer={profilerPortalContainer}
/>
</div>
)}
<div
className={styles.TabContent}
hidden={tab !== 'components'}>
<Components
portalContainer={componentsPortalContainer}
/>
</div>
<div
className={styles.TabContent}
hidden={tab !== 'profiler'}>
<Profiler portalContainer={profilerPortalContainer} />
</div>
</div>
</SchedulingProfilerContextController>
</ProfilerContextController>
</TreeContextController>
</HookNamesContext.Provider>
@@ -21,6 +21,7 @@ export type IconType =
| 'flame-chart'
| 'profiler'
| 'ranked-chart'
| 'scheduling-profiler'
| 'search'
| 'settings'
| 'store-as-global-variable'
@@ -64,6 +65,9 @@ export default function Icon({className = '', type}: Props) {
case 'ranked-chart':
pathData = PATH_RANKED_CHART;
break;
case 'scheduling-profiler':
pathData = PATH_SCHEDULING_PROFILER;
break;
case 'search':
pathData = PATH_SEARCH;
break;
@@ -136,6 +140,11 @@ const PATH_FLAME_CHART = `
const PATH_PROFILER = 'M5 9.2h3V19H5zM10.6 5h2.8v14h-2.8zm5.6 8H19v6h-2.8z';
const PATH_SCHEDULING_PROFILER = `
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
16H5V9h14v10zm0-12H5V5h14v2zM7 11h5v5H7z
`;
const PATH_SEARCH = `
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
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
@@ -16,6 +16,7 @@ import ClearProfilingDataButton from './ClearProfilingDataButton';
import CommitFlamegraph from './CommitFlamegraph';
import CommitRanked from './CommitRanked';
import RootSelector from './RootSelector';
import {SchedulingProfiler} from 'react-devtools-scheduling-profiler/src/SchedulingProfiler';
import RecordToggle from './RecordToggle';
import ReloadAndProfileButton from './ReloadAndProfileButton';
import ProfilingImportExportButtons from './ProfilingImportExportButtons';
@@ -26,6 +27,7 @@ import SettingsModal from 'react-devtools-shared/src/devtools/views/Settings/Set
import SettingsModalContextToggle from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContextToggle';
import {SettingsModalContextController} from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContext';
import portaledContent from '../portaledContent';
import {StoreContext} from '../context';
import styles from './Profiler.css';
@@ -41,8 +43,12 @@ function Profiler(_: {||}) {
supportsProfiling,
} = useContext(ProfilerContext);
const {supportsSchedulingProfiler} = useContext(StoreContext);
let showRightColumn = true;
let view = null;
if (didRecordCommits) {
if (didRecordCommits || selectedTabID === 'scheduling-profiler') {
switch (selectedTabID) {
case 'flame-chart':
view = <CommitFlamegraph />;
@@ -50,6 +56,10 @@ function Profiler(_: {||}) {
case 'ranked-chart':
view = <CommitRanked />;
break;
case 'scheduling-profiler':
view = <SchedulingProfiler />;
showRightColumn = false;
break;
default:
break;
}
@@ -101,7 +111,9 @@ function Profiler(_: {||}) {
currentTab={selectedTabID}
id="Profiler"
selectTab={selectTab}
tabs={tabs}
tabs={
supportsSchedulingProfiler ? tabsWithSchedulingProfiler : tabs
}
type="profiler"
/>
<RootSelector />
@@ -119,7 +131,7 @@ function Profiler(_: {||}) {
<ModalDialog />
</div>
</div>
<div className={styles.RightColumn}>{sidebar}</div>
{showRightColumn && <div className={styles.RightColumn}>{sidebar}</div>}
<SettingsModal />
</div>
</SettingsModalContextController>
@@ -141,6 +153,17 @@ const tabs = [
},
];
const tabsWithSchedulingProfiler = [
...tabs,
null, // Divider/separator
{
id: 'scheduling-profiler',
icon: 'scheduling-profiler',
label: 'Scheduling',
title: 'Scheduling Profiler',
},
];
const NoProfilingData = () => (
<div className={styles.Column}>
<div className={styles.Header}>No profiling data has been recorded.</div>
@@ -19,7 +19,8 @@ import {StoreContext} from '../context';
import type {ProfilingDataFrontend} from './types';
export type TabID = 'flame-chart' | 'ranked-chart';
// TODO (scheduling profiler) Should this be its own context?
export type TabID = 'flame-chart' | 'ranked-chart' | 'scheduling-profiler';
export type Context = {|
// Which tab is selected in the Profiler UI?
@@ -19,13 +19,17 @@ import {
prepareProfilingDataFrontendFromExport,
} from './utils';
import {downloadFile} from '../utils';
import {SchedulingProfilerContext} from 'react-devtools-scheduling-profiler/src/SchedulingProfilerContext';
import styles from './ProfilingImportExportButtons.css';
import type {ProfilingDataExport} from './types';
export default function ProfilingImportExportButtons() {
const {isProfiling, profilingData, rootID} = useContext(ProfilerContext);
const {isProfiling, profilingData, rootID, selectedTabID} = useContext(
ProfilerContext,
);
const {importSchedulingProfilerData} = useContext(SchedulingProfilerContext);
const store = useContext(StoreContext);
const {profilerStore} = store;
@@ -64,13 +68,13 @@ export default function ProfilingImportExportButtons() {
}
}, [rootID, profilingData]);
const uploadData = useCallback(() => {
const clickInputElement = useCallback(() => {
if (inputRef.current !== null) {
inputRef.current.click();
}
}, []);
const handleFiles = useCallback(() => {
const importProfilerData = useCallback(() => {
const input = inputRef.current;
if (input !== null && input.files.length > 0) {
const fileReader = new FileReader();
@@ -104,6 +108,13 @@ export default function ProfilingImportExportButtons() {
}
}, [modalDialogDispatch, profilerStore]);
const importSchedulingProfilerDataWrapper = event => {
const input = inputRef.current;
if (input !== null && input.files.length > 0) {
importSchedulingProfilerData(input.files[0]);
}
};
return (
<Fragment>
<div className={styles.VRule} />
@@ -111,18 +122,26 @@ export default function ProfilingImportExportButtons() {
ref={inputRef}
className={styles.Input}
type="file"
onChange={handleFiles}
onChange={
selectedTabID === 'scheduling-profiler'
? importSchedulingProfilerDataWrapper
: importProfilerData
}
tabIndex={-1}
/>
<a ref={downloadRef} className={styles.Input} />
<Button
disabled={isProfiling}
onClick={uploadData}
onClick={clickInputElement}
title="Load profile...">
<ButtonIcon type="import" />
</Button>
<Button
disabled={isProfiling || !profilerStore.didRecordCommits}
disabled={
isProfiling ||
!profilerStore.didRecordCommits ||
selectedTabID === 'scheduling-profiler'
}
onClick={downloadData}
title="Save profile...">
<ButtonIcon type="export" />
@@ -411,11 +411,16 @@ export function updateThemeVariables(
updateStyleHelper(theme, 'color-record-active', documentElements);
updateStyleHelper(theme, 'color-record-hover', documentElements);
updateStyleHelper(theme, 'color-record-inactive', documentElements);
updateStyleHelper(theme, 'color-resize-bar', documentElements);
updateStyleHelper(theme, 'color-color-scroll-thumb', documentElements);
updateStyleHelper(theme, 'color-color-scroll-track', documentElements);
updateStyleHelper(theme, 'color-search-match', documentElements);
updateStyleHelper(theme, 'color-shadow', documentElements);
updateStyleHelper(theme, 'color-search-match-current', documentElements);
updateStyleHelper(
theme,
'color-scheduling-profiler-flame-graph-label',
documentElements,
);
updateStyleHelper(
theme,
'color-selected-tree-highlight-active',
@@ -426,6 +431,137 @@ export function updateThemeVariables(
'color-selected-tree-highlight-inactive',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-priority-background',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-priority-border',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-user-timing',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-user-timing-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-idle',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-idle-selected',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-idle-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-render',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-render-selected',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-render-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-commit',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-commit-selected',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-commit-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-layout-effects',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-layout-effects-selected',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-layout-effects-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-passive-effects',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-passive-effects-selected',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-passive-effects-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-schedule',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-schedule-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-schedule-cascading',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-schedule-cascading-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-suspend',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-suspend-hover',
documentElements,
);
updateStyleHelper(
theme,
'color-scheduling-profiler-react-work-border',
documentElements,
);
updateStyleHelper(theme, 'color-shadow', documentElements);
updateStyleHelper(theme, 'color-tab-selected-border', documentElements);
updateStyleHelper(theme, 'color-text', documentElements);
updateStyleHelper(theme, 'color-text-invalid', documentElements);
@@ -85,6 +85,14 @@
.TabLabelSettings {
}
.VRule {
height: 20px;
width: 1px;
border-left: 1px solid var(--color-border);
padding-left: 0.25rem;
margin-left: 0.25rem;
}
@media screen and (max-width: 525px) {
.IconSizeNavigation {
margin-right: 0;
+11 -4
View File
@@ -29,7 +29,7 @@ export type Props = {|
disabled?: boolean,
id: string,
selectTab: (tabID: any) => void,
tabs: Array<TabInfo>,
tabs: Array<TabInfo | null>,
type: 'navigation' | 'profiler' | 'settings',
|};
@@ -41,8 +41,9 @@ export default function TabBar({
tabs,
type,
}: Props) {
if (!tabs.some(tab => tab.id === currentTab)) {
selectTab(tabs[0].id);
if (!tabs.some(tab => tab !== null && tab.id === currentTab)) {
const firstTab = ((tabs.find(tab => tab !== null): any): TabInfo);
selectTab(firstTab.id);
}
const onChange = useCallback(
@@ -88,7 +89,13 @@ export default function TabBar({
return (
<Fragment>
{tabs.map(({icon, id, label, title}) => {
{tabs.map(tab => {
if (tab === null) {
return <div className={styles.VRule} />;
}
const {icon, id, label, title} = tab;
let button = (
<label
className={[
@@ -6,6 +6,7 @@
font-size: 12px;
background-color: var(--color-tooltip-background);
color: var(--color-tooltip-text);
box-shadow: 1px 1px 2px var(--color-shadow);
/* Make sure this is above the DevTools, which are above the Overlay */
z-index: 10000002;
@@ -77,6 +77,34 @@
--light-color-record-active: #fc3a4b;
--light-color-record-hover: #3578e5;
--light-color-record-inactive: #0088fa;
--light-color-resize-bar: #cccccc;
--light-color-scheduling-profiler-flame-graph-label: #000000;
--light-color-scheduling-profiler-priority-background: #ededf0;
--light-color-scheduling-profiler-priority-border: #d7d7db;
--light-color-scheduling-profiler-user-timing: #c9cacd;
--light-color-scheduling-profiler-user-timing-hover:#93959a;
--light-color-scheduling-profiler-react-idle: #edf6ff;
--light-color-scheduling-profiler-react-idle-selected:#EDF6FF;
--light-color-scheduling-profiler-react-idle-hover:#EDF6FF;
--light-color-scheduling-profiler-react-render: #9fc3f3;
--light-color-scheduling-profiler-react-render-selected:#64A9F5;
--light-color-scheduling-profiler-react-render-hover:#2683E2;
--light-color-scheduling-profiler-react-commit: #ff718e;
--light-color-scheduling-profiler-react-commit-selected:#FF5277;
--light-color-scheduling-profiler-react-commit-hover:#ed0030;
--light-color-scheduling-profiler-react-layout-effects:#c88ff0;
--light-color-scheduling-profiler-react-layout-effects-selected:#934FC1;
--light-color-scheduling-profiler-react-layout-effects-hover:#601593;
--light-color-scheduling-profiler-react-passive-effects:#c88ff0;
--light-color-scheduling-profiler-react-passive-effects-selected:#934FC1;
--light-color-scheduling-profiler-react-passive-effects-hover:#601593;
--light-color-scheduling-profiler-react-schedule: #9fc3f3;
--light-color-scheduling-profiler-react-schedule-hover:#2683E2;
--light-color-scheduling-profiler-react-schedule-cascading:#ff718e;
--light-color-scheduling-profiler-react-schedule-cascading-hover:#ed0030;
--light-color-scheduling-profiler-react-suspend: #a6e59f;
--light-color-scheduling-profiler-react-suspend-hover:#13bc00;
--light-color-scheduling-profiler-react-work-border:#ffffff;
--light-color-scroll-thumb: #c2c2c2;
--light-color-scroll-track: #fafafa;
--light-color-search-match: yellow;
@@ -146,7 +174,7 @@
--dark-color-console-warning-border: #665500;
--dark-color-console-warning-icon: #f4bd00;
--dark-color-console-warning-text: #f5f2ed;
--dark-color-context-background: rgba(255,255,255,.9);
--dark-color-context-background: rgba(255,255,255,.95);
--dark-color-context-background-hover: rgba(0, 136, 250, 0.1);
--dark-color-context-background-selected: #0088fa;
--dark-color-context-border: #eeeeee;
@@ -169,6 +197,34 @@
--dark-color-record-active: #fc3a4b;
--dark-color-record-hover: #a2e9fc;
--dark-color-record-inactive: #61dafb;
--dark-color-resize-bar: #3d424a;
--dark-color-scheduling-profiler-flame-graph-label: #000000;
--dark-color-scheduling-profiler-priority-background: #1d2129;
--dark-color-scheduling-profiler-priority-border: #282c34;
--dark-color-scheduling-profiler-user-timing: #c9cacd;
--dark-color-scheduling-profiler-user-timing-hover:#93959a;
--dark-color-scheduling-profiler-react-idle: #3d485b;
--dark-color-scheduling-profiler-react-idle-selected:#465269;
--dark-color-scheduling-profiler-react-idle-hover:#465269;
--dark-color-scheduling-profiler-react-render: #9fc3f3;
--dark-color-scheduling-profiler-react-render-selected:#64A9F5;
--dark-color-scheduling-profiler-react-render-hover:#2683E2;
--dark-color-scheduling-profiler-react-commit: #ff718e;
--dark-color-scheduling-profiler-react-commit-selected:#FF5277;
--dark-color-scheduling-profiler-react-commit-hover:#ed0030;
--dark-color-scheduling-profiler-react-layout-effects:#c88ff0;
--dark-color-scheduling-profiler-react-layout-effects-selected:#934FC1;
--dark-color-scheduling-profiler-react-layout-effects-hover:#601593;
--dark-color-scheduling-profiler-react-passive-effects:#c88ff0;
--dark-color-scheduling-profiler-react-passive-effects-selected:#934FC1;
--dark-color-scheduling-profiler-react-passive-effects-hover:#601593;
--dark-color-scheduling-profiler-react-schedule: #9fc3f3;
--dark-color-scheduling-profiler-react-schedule-hover:#2683E2;
--dark-color-scheduling-profiler-react-schedule-cascading:#ff718e;
--dark-color-scheduling-profiler-react-schedule-cascading-hover:#ed0030;
--dark-color-scheduling-profiler-react-suspend: #a6e59f;
--dark-color-scheduling-profiler-react-suspend-hover:#13bc00;
--dark-color-scheduling-profiler-react-work-border:#ffffff;
--dark-color-scroll-thumb: #afb3b9;
--dark-color-scroll-track: #313640;
--dark-color-search-match: yellow;
@@ -184,7 +240,7 @@
--dark-color-toggle-background-on: #178fb9;
--dark-color-toggle-background-off: #777d88;
--dark-color-toggle-text: #ffffff;
--dark-color-tooltip-background: rgba(255, 255, 255, 0.9);
--dark-color-tooltip-background: rgba(255, 255, 255, 0.95);
--dark-color-tooltip-text: #000000;
/* Font smoothing */