@samitouri / QOS-React / commits / 64e355bb74

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;