Added basic Interactions view (without graph for now)
Brian Vaughn committed
Mar 20, 2019 at 11:12 UTC
64e355bb749969e61a4d48af0f2638c5e64a3773
29 files changed
+361
-74
src/backend/agent.js
+1
-6
@@ -102,11 +102,9 @@ export default class Agent extends EventEmitter {
102
};
103
104
getInteractions = ({
105
- commitIndex,
105
rendererID,
106
rootID,
107
}: {
109
- commitIndex: number,
108
rendererID: number,
109
rootID: number,
110
}) => {
@@ -114,10 +112,7 @@ export default class Agent extends EventEmitter {
112
if (renderer == null) {
113
console.warn(`Invalid renderer id "${rendererID}"`);
114
} else {
117
- this._bridge.send(
118
- 'interactions',
119
- renderer.getInteractions(rootID, commitIndex)
120
- );
115
+ this._bridge.send('interactions', renderer.getInteractions(rootID));
116
}
117
};
118
src/backend/renderer.js
+1
-1
@@ -1425,7 +1425,7 @@ export function attach(
1425
};
1426
}
1427
1428
- function getInteractions(rootID: number, commitIndex: number): Interactions {
1428
+ function getInteractions(rootID: number): Interactions {
1429
const commitProfilingMetadata = ((rootToCommitProfilingMetadataMap: any): CommitProfilingMetadataMap).get(
1430
rootID
1431
);
src/backend/types.js
+1
-1
@@ -88,7 +88,7 @@ export type RendererInterface = {
88
component: NativeType,
89
findNearestUnfilteredAncestor?: boolean
90
) => number | null,
91
- getInteractions: (rootID: number, commitIndex: number) => Interactions,
91
+ getInteractions: (rootID: number) => Interactions,
92
getProfilingSummary: (rootID: number) => ProfilingSummary,
93
handleCommitFiberRoot: (fiber: Object) => void,
94
handleCommitFiberUnmount: (fiber: Object) => void,
src/devtools/views/Elements/Element.css
+4
-3
@@ -9,16 +9,17 @@
9
user-select: none;
10
}
11
.Element:hover {
12
- background-color: var(--color-tree-node-hover);
12
+ background-color: var(--color-hover-background);
13
}
14
15
.SelectedElement {
16
- background-color: var(--color-tree-node-selected);
16
+ background-color: var(--color-selected-background);
17
+ color: var(--color-selected-foreground);
18
19
/* Invert colors */
20
--color-component-name: var(--color-component-name-inverted);
21
--color-jsx-arrow-brackets: var(--color-jsx-arrow-brackets-inverted);
21
- --color-attribute-name: var(--color-tree-node-hover);
22
+ --color-attribute-name: var(--color-hover-background);
23
--color-attribute-value: var(--color-component-name-inverted);
24
}
25
src/devtools/views/Elements/OwnersStack.css
+3
-3
@@ -21,12 +21,12 @@
21
color: var(--color-component-name);
22
}
23
.Component:hover {
24
- background-color: var(--color-tree-node-hover);
24
+ background-color: var(--color-hover-background);
25
}
26
27
.FocusedComponent {
28
- background-color: var(--color-tree-node-selected);
29
- color: var(--color-component-name-inverted);
28
+ background-color: var(--color-selected-background);
29
+ color: var(--color-selected-foreground);
30
}
31
32
.VRule {
src/devtools/views/Profiler/CommitFlamegraph.js
+1
-1
@@ -126,7 +126,7 @@ function CommitFlamegraph({
126
// If a commit contains no fibers with an actualDuration > 0,
127
// Display a fallback message.
128
if (chartData.depth === 0) {
129
- return <NoCommitData />;
129
+ return <NoCommitData height={height} width={width} />;
130
}
131
132
return (
src/devtools/views/Profiler/CommitFlamegraphListItem.js
+8
-7
@@ -1,21 +1,20 @@
1
// @flow
2
3
-import React, { Fragment, useCallback } from 'react';
3
+import React, { Fragment, memo, useCallback } from 'react';
4
+import { areEqual } from 'react-window';
5
import { barHeight, barWidthThreshold } from './constants';
6
import { getGradientColor } from './utils';
7
import ChartNode from './ChartNode';
8
9
import type { ItemData } from './CommitFlamegraph';
10
10
-export default function CommitFlamegraphListItem({
11
- data,
12
- index,
13
- style,
14
-}: {
11
+type Props = {
12
data: ItemData,
13
index: number,
14
style: Object,
18
-}) {
15
+};
16
+
17
+function CommitFlamegraphListItem({ data, index, style }: Props) {
18
const {
19
chartData,
20
scaleX,
@@ -100,3 +99,5 @@ export default function CommitFlamegraphListItem({
99
</Fragment>
100
);
101
}
102
+
103
+export default memo<Props>(CommitFlamegraphListItem, areEqual);
src/devtools/views/Profiler/CommitRanked.js
+1
-1
@@ -98,7 +98,7 @@ function CommitRanked({ height, width }: {| height: number, width: number |}) {
98
// If a commit contains no fibers with an actualDuration > 0,
99
// Display a fallback message.
100
if (chartData.nodes.length === 0) {
101
- return <NoCommitData />;
101
+ return <NoCommitData height={height} width={width} />;
102
}
103
104
return (
src/devtools/views/Profiler/CommitRankedListItem.js
+8
-7
@@ -1,21 +1,20 @@
1
// @flow
2
3
-import React, { useCallback } from 'react';
3
+import React, { memo, useCallback } from 'react';
4
+import { areEqual } from 'react-window';
5
import { barHeight, minBarWidth } from './constants';
6
import { getGradientColor } from './utils';
7
import ChartNode from './ChartNode';
8
9
import type { ItemData } from './CommitRanked';
10
10
-export default function CommitRankedListItem({
11
- data,
12
- index,
13
- style,
14
-}: {
11
+type Props = {
12
data: ItemData,
13
index: number,
14
style: Object,
18
-}) {
15
+};
16
+
17
+function CommitRankedListItem({ data, index, style }: Props) {
18
const { chartData, scaleX, selectedFiberIndex, selectFiber, width } = data;
19
20
const node = chartData.nodes[index];
@@ -48,3 +47,5 @@ export default function CommitRankedListItem({
47
/>
48
);
49
}
50
+
51
+export default memo<Props>(CommitRankedListItem, areEqual);
src/devtools/views/Profiler/InteractionListItem.css
new
+16
@@ -0,0 +1,16 @@
1
+.Interaction,
2
+.SelectedInteraction {
3
+ display: flex;
4
+ align-items: center;
5
+ padding: 0 0.25rem;
6
+ border-bottom: 1px solid var(--color-border);
7
+}
8
+
9
+.Interaction:hover {
10
+ background-color: var(--color-hover-background);
11
+}
12
+
13
+.SelectedInteraction {
14
+ background-color: var(--color-selected-background);
15
+ color: var(--color-selected-foreground);
16
+}
src/devtools/views/Profiler/InteractionListItem.js
new
+43
@@ -0,0 +1,43 @@
1
+// @flow
2
+
3
+import React, { memo, useCallback } from 'react';
4
+import { areEqual } from 'react-window';
5
+
6
+import styles from './InteractionListItem.css';
7
+
8
+import type { ItemData } from './Interactions';
9
+
10
+type Props = {
11
+ data: ItemData,
12
+ index: number,
13
+ style: Object,
14
+};
15
+
16
+function InteractionListItem({ data: itemData, index, style }: Props) {
17
+ const { interactions, selectedInteractionID, selectInteraction } = itemData;
18
+
19
+ const interaction = interactions[index];
20
+
21
+ const handleClick = useCallback(() => {
22
+ selectInteraction(interaction.id);
23
+ }, [interaction, selectInteraction]);
24
+
25
+ // TODO (profiling Render commit bar)
26
+
27
+ return (
28
+ <div
29
+ className={
30
+ selectedInteractionID === interaction.id
31
+ ? styles.SelectedInteraction
32
+ : styles.Interaction
33
+ }
34
+ onClick={handleClick}
35
+ style={style}
36
+ title={interaction.name}
37
+ >
38
+ {interaction.name}
39
+ </div>
40
+ );
41
+}
42
+
43
+export default memo<Props>(InteractionListItem, areEqual);
src/devtools/views/Profiler/Interactions.css
new
+5
@@ -0,0 +1,5 @@
1
+.Container {
2
+ width: 100%;
3
+ height: 100%;
4
+ padding: 0.5rem;
5
+}
src/devtools/views/Profiler/Interactions.js
new
+73
@@ -0,0 +1,73 @@
1
+// @flow
2
+
3
+import React, { useContext, useMemo } from 'react';
4
+import AutoSizer from 'react-virtualized-auto-sizer';
5
+import { FixedSizeList } from 'react-window';
6
+import { ProfilerContext } from './ProfilerContext';
7
+import InteractionListItem from './InteractionListItem';
8
+import NoInteractions from './NoInteractions';
9
+import { StoreContext } from '../context';
10
+
11
+import styles from './Interactions.css';
12
+
13
+import type { InteractionWithCommits } from './types';
14
+
15
+export type ItemData = {|
16
+ interactions: Array<InteractionWithCommits>,
17
+ selectedInteractionID: number | null,
18
+ selectInteraction: (id: number | null) => void,
19
+|};
20
+
21
+export default function InteractionsAutoSizer(_: {||}) {
22
+ return (
23
+ <div className={styles.Container}>
24
+ <AutoSizer>
25
+ {({ height, width }) => <Interactions height={height} width={width} />}
26
+ </AutoSizer>
27
+ </div>
28
+ );
29
+}
30
+
31
+function Interactions({ height, width }: {| height: number, width: number |}) {
32
+ const {
33
+ rendererID,
34
+ rootID,
35
+ selectedInteractionID,
36
+ selectInteraction,
37
+ } = useContext(ProfilerContext);
38
+ const { profilingCache } = useContext(StoreContext);
39
+
40
+ const { interactions } = profilingCache.Interactions.read({
41
+ rendererID: ((rendererID: any): number),
42
+ rootID: ((rootID: any): number),
43
+ });
44
+
45
+ const itemData = useMemo<ItemData>(
46
+ () => ({
47
+ interactions,
48
+ selectedInteractionID,
49
+ selectInteraction,
50
+ }),
51
+ [interactions, selectedInteractionID, selectInteraction]
52
+ );
53
+
54
+ // TODO (profiling) Up/down arrow keys to select prev/next interaction.
55
+
56
+ // If a commit contains no fibers with an actualDuration > 0,
57
+ // Display a fallback message.
58
+ if (interactions.length === 0) {
59
+ return <NoInteractions height={height} width={width} />;
60
+ }
61
+
62
+ return (
63
+ <FixedSizeList
64
+ height={height}
65
+ itemCount={interactions.length}
66
+ itemData={itemData}
67
+ itemSize={30}
68
+ width={width}
69
+ >
70
+ {InteractionListItem}
71
+ </FixedSizeList>
72
+ );
73
+}
src/devtools/views/Profiler/NoCommitData.css
+1
@@ -2,6 +2,7 @@
2
width: 100%;
3
height: 100%;
4
display: flex;
5
+ flex-direction: column;
6
align-items: center;
7
justify-content: center;
8
}
src/devtools/views/Profiler/NoCommitData.js
+8
-2
@@ -4,9 +4,15 @@ import React from 'react';
4
5
import styles from './NoCommitData.css';
6
7
-export default function NoCommitData() {
7
+export default function NoCommitData({
8
+ height,
9
+ width,
10
+}: {|
11
+ height: number,
12
+ width: number,
13
+|}) {
14
return (
9
- <div className={styles.NoCommitData}>
15
+ <div className={styles.NoCommitData} style={{ height, width }}>
16
<p className={styles.Header}>
17
There is no timing data to display for the currently selected commit.
18
</p>
src/devtools/views/Profiler/NoInteractions.css
new
+16
@@ -0,0 +1,16 @@
1
+.NoInteractions {
2
+ width: 100%;
3
+ height: 100%;
4
+ display: flex;
5
+ flex-direction: column;
6
+ align-items: center;
7
+ justify-content: center;
8
+}
9
+
10
+.Header {
11
+ font-size: var(--font-size-sans-large);
12
+}
13
+
14
+.Link {
15
+ color: var(--color-button);
16
+}
src/devtools/views/Profiler/NoInteractions.js
new
+29
@@ -0,0 +1,29 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+
5
+import styles from './NoInteractions.css';
6
+
7
+export default function NoInteractions({
8
+ height,
9
+ width,
10
+}: {|
11
+ height: number,
12
+ width: number,
13
+|}) {
14
+ return (
15
+ <div className={styles.NoInteractions} style={{ height, width }}>
16
+ <p className={styles.Header}>No interactions were recorded.</p>
17
+ <p>
18
+ <a
19
+ className={styles.Link}
20
+ href="http://fb.me/react-interaction-tracing"
21
+ rel="noopener noreferrer"
22
+ target="_blank"
23
+ >
24
+ Learn more about the interaction tracing API here.
25
+ </a>
26
+ </p>
27
+ </div>
28
+ );
29
+}
src/devtools/views/Profiler/Profiler.css
+4
@@ -87,3 +87,7 @@
87
font-size: var(--font-size-sans-large);
88
color: var(--color-dim);
89
}
90
+
91
+.Link {
92
+ color: var(--color-button);
93
+}
src/devtools/views/Profiler/Profiler.js
+11
-14
@@ -9,7 +9,9 @@ import TabBar from '../TabBar';
9
import CommitFlamegraph from './CommitFlamegraph';
10
import CommitRanked from './CommitRanked';
11
import FilterModal from './FilterModal';
12
+import Interactions from './Interactions';
13
import RecordToggle from './RecordToggle';
14
+import ReloadAndProfileButton from './ReloadAndProfileButton';
15
import SnapshotSelector from './SnapshotSelector';
16
import SidebarCommitInfo from './SidebarCommitInfo';
17
import SidebarInteractions from './SidebarInteractions';
@@ -76,10 +78,7 @@ function NonSuspendingProfiler({
78
<div className={styles.LeftColumn}>
79
<div className={styles.Toolbar}>
80
<RecordToggle disabled={!supportsProfiling} />
79
- <Button disabled title="Reload and start profiling">
80
- {/* TODO (profiling) Wire up reload button */}
81
- <ButtonIcon type="reload" />
82
- </Button>
81
+ <ReloadAndProfileButton />
82
<div className={styles.VRule} />
83
<TabBar
84
currentTab={null}
@@ -105,14 +104,14 @@ function ProfilerFallback() {
104
// This view's subtree uses suspense to request profiler data from the backend.
105
// NOTE that the structure of this UI should mirror NonSuspendingProfiler.
106
function SuspendingProfiler() {
108
- const [tab, setTab] = useState('flame-chart');
107
+ const { selectedTabID, selectTab } = useContext(ProfilerContext);
108
const [isFilterModalShowing, setIsFilterModalShowing] = useState(false);
109
110
const showFilterModal = useCallback(() => setIsFilterModalShowing(true));
111
const dismissFilterModal = useCallback(() => setIsFilterModalShowing(false));
112
113
let view = null;
115
- switch (tab) {
114
+ switch (selectedTabID) {
115
case 'flame-chart':
116
view = <CommitFlamegraph />;
117
break;
@@ -120,14 +119,14 @@ function SuspendingProfiler() {
119
view = <CommitRanked />;
120
break;
121
case 'interactions':
123
- view = <div>Coming soon: Interactions</div>; // TODO (profiling)
122
+ view = <Interactions />;
123
break;
124
default:
125
break;
126
}
127
128
let sidebar = null;
130
- switch (tab) {
129
+ switch (selectedTabID) {
130
case 'interactions':
131
sidebar = <SidebarInteractions />;
132
break;
@@ -144,15 +143,12 @@ function SuspendingProfiler() {
143
<div className={styles.LeftColumn}>
144
<div className={styles.Toolbar}>
145
<RecordToggle />
147
- <Button disabled title="Reload and start profiling">
148
- {/* TODO (profiling) Wire up reload button */}
149
- <ButtonIcon type="reload" />
150
- </Button>
146
+ <ReloadAndProfileButton />
147
<div className={styles.VRule} />
148
<TabBar
153
- currentTab={tab}
149
+ currentTab={selectedTabID}
150
id="Profiler"
155
- selectTab={setTab}
151
+ selectTab={selectTab}
152
size="small"
153
tabs={tabs}
154
/>
@@ -231,6 +227,7 @@ const ProfilingNotSupported = () => (
227
<p>
228
Learn more at{' '}
229
<a
230
+ className={styles.Link}
231
href="https://fb.me/react-profiling"
232
rel="noopener noreferrer"
233
target="_blank"
src/devtools/views/Profiler/ProfilerContext.js
+38
-11
@@ -7,12 +7,19 @@ import React, {
7
useMemo,
8
useState,
9
} from 'react';
10
+import { unstable_batchedUpdates as batchedUpdates } from 'react-dom';
11
import { useLocalStorage, useSubscription } from '../hooks';
12
import { TreeContext } from '../Elements/TreeContext';
13
import { StoreContext } from '../context';
14
import Store from '../../store';
15
16
+export type TabID = 'flame-chart' | 'ranked-chart' | 'interactions';
17
+
18
type Context = {|
19
+ // Which tab is selexted in the Profiler UI?
20
+ selectedTabID: TabID,
21
+ selectTab(id: TabID): void,
22
+
23
// Have we recorded any profiling data?
24
// Are we currently profiling?
25
// This value may be modified by the record button in the Profiler toolbar,
@@ -48,6 +55,10 @@ type Context = {|
55
// Which fiber is currently selected in the Ranked or Flamegraph charts?
56
selectedFiberID: number | null,
57
selectFiber: (id: number | null) => void,
58
+
59
+ // Which interaction is currently selected in the Interactions graph?
60
+ selectedInteractionID: number | null,
61
+ selectInteraction: (id: number | null) => void,
62
|};
63
64
const ProfilerContext = createContext<Context>(((null: any): Context));
@@ -84,10 +95,6 @@ function ProfilerContextController({ children }: Props) {
95
Store
96
>(subscription);
97
87
- // TODO (profiling) The browser extension is a multi-root app,
88
- // so it won't work for the "Profiling" root to depend on a value that's set by the "Elements" root.
89
- // We'll either need to lift that state up into the (shared) Store,
90
- // or use a portal to share the contexts themselves between Chrome tabs.
98
let rendererID = null;
99
let rootID = null;
100
let rootHasProfilingData = false;
@@ -121,20 +128,31 @@ function ProfilerContextController({ children }: Props) {
128
const [selectedCommitIndex, setSelectedCommitIndex] = useState<number | null>(
129
null
130
);
124
-
131
+ const [selectedTabID, selectTab] = useState<TabID>('flame-chart');
132
const [selectedFiberID, selectFiber] = useState<number | null>(null);
133
+ const [selectedInteractionID, selectInteraction] = useState<number | null>(
134
+ null
135
+ );
136
137
if (isProfiling) {
128
- if (selectedFiberID !== null) {
129
- selectFiber(null);
130
- }
131
- if (selectedCommitIndex !== null) {
132
- setSelectedCommitIndex(null);
133
- }
138
+ batchedUpdates(() => {
139
+ if (selectedCommitIndex !== null) {
140
+ setSelectedCommitIndex(null);
141
+ }
142
+ if (selectedFiberID !== null) {
143
+ selectFiber(null);
144
+ }
145
+ if (selectedInteractionID !== null) {
146
+ selectInteraction(null);
147
+ }
148
+ });
149
}
150
151
const value = useMemo(
152
() => ({
153
+ selectedTabID,
154
+ selectTab,
155
+
156
hasProfilingData,
157
isProfiling,
158
startProfiling,
@@ -154,8 +172,14 @@ function ProfilerContextController({ children }: Props) {
172
173
selectedFiberID,
174
selectFiber,
175
+
176
+ selectedInteractionID,
177
+ selectInteraction,
178
}),
179
[
180
+ selectedTabID,
181
+ selectTab,
182
+
183
hasProfilingData,
184
isProfiling,
185
startProfiling,
@@ -175,6 +199,9 @@ function ProfilerContextController({ children }: Props) {
199
200
selectedFiberID,
201
selectFiber,
202
+
203
+ selectedInteractionID,
204
+ selectInteraction,
205
]
206
);
207
src/devtools/views/Profiler/ReloadAndProfileButton.js
new
+14
@@ -0,0 +1,14 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+import Button from '../Button';
5
+import ButtonIcon from '../ButtonIcon';
6
+
7
+export default function ReloadAndProfileButton() {
8
+ // TODO (profiling) Wire up reload button
9
+ return (
10
+ <Button disabled title="Reload and start profiling">
11
+ <ButtonIcon type="reload" />
12
+ </Button>
13
+ );
14
+}
src/devtools/views/Profiler/SidebarInteractions.css
+16
@@ -11,3 +11,19 @@
11
.Content {
12
padding: 0.5rem;
13
}
14
+
15
+.Name {
16
+ font-size: var(--font-size-sans-large);
17
+ white-space: nowrap;
18
+ overflow-x: hidden;
19
+ text-overflow: ellipsis;
20
+}
21
+
22
+.NothingSelected {
23
+ border-top: 1px solid var(--color-border);
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ height: 100%;
28
+ color: var(--color-dim);
29
+}
src/devtools/views/Profiler/SidebarInteractions.js
+37
-3
@@ -2,23 +2,57 @@
2
3
import React, { Fragment, useContext } from 'react';
4
import { ProfilerContext } from './ProfilerContext';
5
+import { formatDuration, formatTime } from './utils';
6
import { StoreContext } from '../context';
7
8
import styles from './SidebarInteractions.css';
9
10
+import type { InteractionWithCommits } from './types';
11
+
12
export type Props = {||};
13
14
export default function SidebarInteractions(_: Props) {
12
- const { selectedCommitIndex, rendererID, rootID } = useContext(
15
+ const { selectedInteractionID, rendererID, rootID } = useContext(
16
ProfilerContext
17
);
18
19
const { profilingCache } = useContext(StoreContext);
20
21
+ if (selectedInteractionID === null) {
22
+ return <div className={styles.NothingSelected}>Nothing selected</div>;
23
+ }
24
+
25
+ const { interactions } = profilingCache.Interactions.read({
26
+ rendererID: ((rendererID: any): number),
27
+ rootID: ((rootID: any): number),
28
+ });
29
+ const { commitDurations, commitTimes } = profilingCache.ProfilingSummary.read(
30
+ {
31
+ rendererID: ((rendererID: any): number),
32
+ rootID: ((rootID: any): number),
33
+ }
34
+ );
35
+
36
+ const interaction = ((interactions.find(
37
+ interaction => interaction.id === selectedInteractionID
38
+ ): any): InteractionWithCommits);
39
+
40
return (
41
<Fragment>
20
- <div className={styles.Toolbar}>Interaction</div>
21
- <div className={styles.Content}>Coming soon</div>
42
+ <div className={styles.Toolbar}>
43
+ <div className={styles.Name}>{interaction.name}</div>
44
+ </div>
45
+ <div className={styles.Content}>
46
+ <ul>
47
+ {interaction.commits.map(commitIndex => (
48
+ <li key={commitIndex}>
49
+ timestamp: {formatTime(commitTimes[commitIndex])}s
50
+ <br />
51
+ duration: {formatDuration(commitDurations[commitIndex])}ms
52
+ </li>
53
+ ))}
54
+ </ul>
55
+ </div>
56
</Fragment>
57
);
58
}
src/devtools/views/Profiler/SnapshotCommitListItem.js
+1
-1
@@ -52,7 +52,7 @@ function SnapshotCommitListItem({ data: itemData, index, style }: Props) {
52
...style,
53
width,
54
borderBottom: isSelected
55
- ? '3px solid var(--color-tree-node-selected)'
55
+ ? '3px solid var(--color-selected-border)'
56
: undefined,
57
}}
58
title={`Duration ${formatDuration(commitDuration)}ms at ${formatTime(
src/devtools/views/Profiler/types.js
+6
-4
@@ -20,11 +20,13 @@ export type Interaction = {|
20
timestamp: number,
21
|};
22
23
+export type InteractionWithCommits = {|
24
+ ...Interaction,
25
+ commits: Array<number>,
26
+|};
27
+
28
export type Interactions = {|
24
- interactions: Array<{|
25
- ...Interaction,
26
- commits: Array<number>,
27
- |}>,
29
+ interactions: Array<InteractionWithCommits>,
30
rootID: number,
31
|};
32
src/devtools/views/Settings/Settings.css
+1
-1
@@ -38,7 +38,7 @@
38
}
39
40
.Option:hover {
41
- background-color: var(--color-tree-node-hover);
41
+ background-color: var(--color-hover-background);
42
}
43
44
.Option:first-of-type {
src/devtools/views/Settings/SettingsContext.js
+4
-2
@@ -219,6 +219,7 @@ function updateThemeVariables(
219
updateStyleHelper(theme, 'color-dim', documentElements);
220
updateStyleHelper(theme, 'color-dimmer', documentElements);
221
updateStyleHelper(theme, 'color-dimmest', documentElements);
222
+ updateStyleHelper(theme, 'color-hover-background', documentElements);
223
updateStyleHelper(theme, 'color-jsx-arrow-brackets', documentElements);
224
updateStyleHelper(
225
theme,
@@ -229,8 +230,9 @@ function updateThemeVariables(
230
updateStyleHelper(theme, 'color-record-active', documentElements);
231
updateStyleHelper(theme, 'color-record-hover', documentElements);
232
updateStyleHelper(theme, 'color-record-inactive', documentElements);
232
- updateStyleHelper(theme, 'color-tree-node-selected', documentElements);
233
- updateStyleHelper(theme, 'color-tree-node-hover', documentElements);
233
+ updateStyleHelper(theme, 'color-selected-background', documentElements);
234
+ updateStyleHelper(theme, 'color-selected-border', documentElements);
235
+ updateStyleHelper(theme, 'color-selected-foreground', documentElements);
236
updateStyleHelper(theme, 'color-search-match', documentElements);
237
updateStyleHelper(theme, 'color-search-match-current', documentElements);
238
updateStyleHelper(theme, 'color-text-color', documentElements);
src/devtools/views/TabBar.css
+2
-2
@@ -13,11 +13,11 @@
13
}
14
.Tab:hover,
15
.TabCurrent:hover {
16
- background-color: var(--color-tree-node-hover);
16
+ background-color: var(--color-hover-background);
17
}
18
19
.TabCurrent {
20
- border-bottom: 3px solid var(--color-tree-node-selected);
20
+ border-bottom: 3px solid var(--color-selected-border);
21
}
22
23
.TabDisabled {
src/devtools/views/root.css
+8
-4
@@ -33,6 +33,7 @@
33
--light-color-dim: #777d88;
34
--light-color-dimmer: #cfd1d5;
35
--light-color-dimmest: #eff0f1;
36
+ --light-color-hover-background: #ebf1fb;
37
--light-color-jsx-arrow-brackets: #333333;
38
--light-color-jsx-arrow-brackets-inverted: rgba(255, 255, 255, 0.7);
39
--light-color-modal-background: rgba(255, 255, 255, 0.25);
@@ -41,9 +42,10 @@
42
--light-color-record-inactive: #cfd1d5;
43
--light-color-search-match: yellow;
44
--light-color-search-match-current: #f7923b;
45
+ --light-color-selected-background: #0088fa;
46
+ --light-color-selected-border: #0088fa;
47
+ --light-color-selected-foreground: #ffffff;
48
--light-color-text-color: #000000;
45
- --light-color-tree-node-selected: #0088fa;
46
- --light-color-tree-node-hover: #ebf1fb;
49
50
/* Dark theme */
51
--dark-color-attribute-name: #9d87d2;
@@ -75,6 +77,7 @@
77
--dark-color-dim: #8f949d;
78
--dark-color-dimmer: #777d88;
79
--dark-color-dimmest: #4f5766;
80
+ --dark-color-hover-background: #3d424a;
81
--dark-color-jsx-arrow-brackets: #777d88;
82
--dark-color-jsx-arrow-brackets-inverted: rgba(255, 255, 255, 0.7);
83
--dark-color-modal-background: rgba(0, 0, 0, 0.25);
@@ -83,9 +86,10 @@
86
--dark-color-record-inactive: #777d88;
87
--dark-color-search-match: yellow;
88
--dark-color-search-match-current: #f7923b;
89
+ --dark-color-selected-background: #178fb9;
90
+ --dark-color-selected-border: #178fb9;
91
+ --dark-color-selected-foreground: #ffffff;
92
--dark-color-text-color: #ffffff;
87
- --dark-color-tree-node-selected: #178fb9;
88
- --dark-color-tree-node-hover: #3d424a;
93
94
/* Compact density */
95
--compact-font-size-monospace-normal: 11px;