diff --git a/shells/browser/chrome/manifest.json b/shells/browser/chrome/manifest.json index ccbf29aa3c..c22e4b3849 100644 --- a/shells/browser/chrome/manifest.json +++ b/shells/browser/chrome/manifest.json @@ -27,13 +27,7 @@ "devtools_page": "main.html", "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", - "web_accessible_resources": [ - "elements.html", - "main.html", - "profiler.html", - "settings.html", - "build/backend.js" - ], + "web_accessible_resources": ["main.html", "panel.html", "build/backend.js"], "background": { "scripts": ["build/background.js"], diff --git a/shells/browser/firefox/manifest.json b/shells/browser/firefox/manifest.json index a1653d5005..e0015a59ff 100644 --- a/shells/browser/firefox/manifest.json +++ b/shells/browser/firefox/manifest.json @@ -33,13 +33,7 @@ "devtools_page": "main.html", "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", - "web_accessible_resources": [ - "elements.html", - "main.html", - "profiler.html", - "settings.html", - "build/backend.js" - ], + "web_accessible_resources": ["main.html", "panel.html", "build/backend.js"], "background": { "scripts": ["build/background.js"], diff --git a/shells/browser/shared/build.js b/shells/browser/shared/build.js index 3923c1f128..846f739670 100644 --- a/shells/browser/shared/build.js +++ b/shells/browser/shared/build.js @@ -7,14 +7,7 @@ const { join } = require('path'); // These files are copied along with Webpack-bundled files // to produce the final web extension -const STATIC_FILES = [ - 'icons', - 'popups', - 'elements.html', - 'main.html', - 'profiler.html', - 'settings.html', -]; +const STATIC_FILES = ['icons', 'popups', 'main.html', 'panel.html']; const preProcess = async (destinationPath, tempPath) => { await remove(destinationPath); // Clean up from previously completed builds diff --git a/shells/browser/shared/elements.html b/shells/browser/shared/panel.html similarity index 93% rename from shells/browser/shared/elements.html rename to shells/browser/shared/panel.html index bbaa81a0f2..60fd1bdf13 100644 --- a/shells/browser/shared/elements.html +++ b/shells/browser/shared/panel.html @@ -27,6 +27,6 @@
Unable to find React on the page.
- + diff --git a/shells/browser/shared/profiler.html b/shells/browser/shared/profiler.html deleted file mode 100644 index 0281d9d2cc..0000000000 --- a/shells/browser/shared/profiler.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - -
Unable to find React on the page.
- - - diff --git a/shells/browser/shared/settings.html b/shells/browser/shared/settings.html deleted file mode 100644 index 16beacccea..0000000000 --- a/shells/browser/shared/settings.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - -
Unable to find React on the page.
- - - diff --git a/shells/browser/shared/src/main.js b/shells/browser/shared/src/main.js index 9183a0c4ce..0a596e42ca 100644 --- a/shells/browser/shared/src/main.js +++ b/shells/browser/shared/src/main.js @@ -1,8 +1,16 @@ /* global chrome */ +import { createElement } from 'react'; +import { unstable_createRoot as createRoot, flushSync } from 'react-dom'; import Bridge from 'src/bridge'; import Store from 'src/devtools/Store'; import inject from './inject'; +import { + createViewElementSource, + getBrowserName, + getBrowserTheme, +} from './utils'; +import DevTools from 'src/devtools/views/DevTools'; let panelCreated = false; @@ -24,9 +32,15 @@ function createPanelIfReactLoaded() { let bridge = null; let store = null; - let elementsPanel = null; - let profilerPanel = null; - let settingsPanel = null; + + let elementsPortalContainer = null; + let profilerPortalContainer = null; + let settingsPortalContainer = null; + + let cloneStyleTags = null; + let mostRecentOverrideTab = null; + let render = null; + let root = null; function initBridgeAndStore() { let hasPortBeenDisconnected = false; @@ -54,69 +68,86 @@ function createPanelIfReactLoaded() { // Otherwise the Store may miss important initial tree op codes. inject(chrome.runtime.getURL('build/backend.js')); - if (elementsPanel !== null) { - elementsPanel.injectBridgeAndStore(bridge, store); - } - if (profilerPanel !== null) { - profilerPanel.injectBridgeAndStore(bridge, store); - } - if (settingsPanel !== null) { - settingsPanel.injectBridgeAndStore(bridge, store); - } + const viewElementSource = createViewElementSource(bridge, store); + + root = createRoot(document.createElement('div')); + + render = (overrideTab = mostRecentOverrideTab) => { + mostRecentOverrideTab = overrideTab; + + root.render( + createElement(DevTools, { + bridge, + browserName: getBrowserName(), + browserTheme: getBrowserTheme(), + elementsPortalContainer, + overrideTab, + profilerPortalContainer, + settingsPortalContainer, + showTabBar: false, + store, + viewElementSource, + }) + ); + }; + + render(); } + cloneStyleTags = () => { + const linkTags = []; + for (let linkTag of document.getElementsByTagName('link')) { + if (linkTag.rel === 'stylesheet') { + const newLinkTag = document.createElement('link'); + for (let attribute of linkTag.attributes) { + newLinkTag.setAttribute(attribute.nodeName, attribute.nodeValue); + } + linkTags.push(newLinkTag); + } + } + return linkTags; + }; + initBridgeAndStore(); - chrome.devtools.panels.create( - '⚛ Elements', - '', - 'elements.html', - panel => { - panel.onShown.addListener(panel => { - if (elementsPanel === null) { - panel.injectBridgeAndStore(bridge, store); - } + chrome.devtools.panels.create('⚛ Elements', '', 'panel.html', panel => { + panel.onShown.addListener(panel => { + elementsPortalContainer = panel.container; + if (elementsPortalContainer != null) { + elementsPortalContainer.innerHTML = ''; + render('elements'); + panel.injectStyles(cloneStyleTags); + } - elementsPanel = panel; - - // TODO: When the user switches to the panel, check for an Elements tab selection. - }); - panel.onHidden.addListener(() => { - // TODO: Stop highlighting and stuff. - }); - } - ); + // TODO: When the user switches to the panel, check for an Elements tab selection. + }); + panel.onHidden.addListener(() => { + // TODO: Stop highlighting and stuff. + }); + }); // TODO (profiling) Is there a way to detect profiling support and conditionally register this panel? - chrome.devtools.panels.create( - '⚛ Profiler', - '', - 'profiler.html', - panel => { - panel.onShown.addListener(panel => { - if (settingsPanel === null) { - panel.injectBridgeAndStore(bridge, store); - } + chrome.devtools.panels.create('⚛ Profiler', '', 'panel.html', panel => { + panel.onShown.addListener(panel => { + profilerPortalContainer = panel.container; + if (profilerPortalContainer != null) { + profilerPortalContainer.innerHTML = ''; + render('profiler'); + panel.injectStyles(cloneStyleTags); + } + }); + }); - profilerPanel = panel; - }); - } - ); - - chrome.devtools.panels.create( - '⚛ Settings', - '', - 'settings.html', - panel => { - panel.onShown.addListener(panel => { - if (settingsPanel === null) { - panel.injectBridgeAndStore(bridge, store); - } - - settingsPanel = panel; - }); - } - ); + chrome.devtools.panels.create('⚛ Settings', '', 'panel.html', panel => { + panel.onShown.addListener(panel => { + settingsPortalContainer = panel.container; + if (settingsPortalContainer != null) { + settingsPortalContainer.innerHTML = ''; + render('settings'); + panel.injectStyles(cloneStyleTags); + } + }); + }); chrome.devtools.network.onNavigated.removeListener(checkPageForReact); @@ -124,7 +155,9 @@ function createPanelIfReactLoaded() { chrome.devtools.network.onNavigated.addListener(function onNavigated() { bridge.send('shutdown'); - initBridgeAndStore(); + // It's easiest to recreate the DevTools panel (to clean up potential stale state). + // We can revisit this in the future as a small optimization. + flushSync(() => root.unmount(initBridgeAndStore)); }); } ); diff --git a/shells/browser/shared/src/panel.js b/shells/browser/shared/src/panel.js new file mode 100644 index 0000000000..f84ac11beb --- /dev/null +++ b/shells/browser/shared/src/panel.js @@ -0,0 +1,18 @@ +// Portal target container. +window.container = document.getElementById('container'); + +let hasInjectedStyles = false; + +// DevTools styles are injected into the top-level document head (where the main React app is rendered). +// This method copies those styles to the child window where each panel (e.g. Elements, Profiler) is portaled. +window.injectStyles = getLinkTags => { + if (!hasInjectedStyles) { + hasInjectedStyles = true; + + const linkTags = getLinkTags(); + + for (let linkTag of linkTags) { + document.head.appendChild(linkTag); + } + } +}; diff --git a/shells/browser/shared/src/panels/elements.js b/shells/browser/shared/src/panels/elements.js deleted file mode 100644 index ef7f5e80df..0000000000 --- a/shells/browser/shared/src/panels/elements.js +++ /dev/null @@ -1,3 +0,0 @@ -import { createPanel } from './utils'; - -createPanel('elements'); diff --git a/shells/browser/shared/src/panels/profiler.js b/shells/browser/shared/src/panels/profiler.js deleted file mode 100644 index 23f028455b..0000000000 --- a/shells/browser/shared/src/panels/profiler.js +++ /dev/null @@ -1,3 +0,0 @@ -import { createPanel } from './utils'; - -createPanel('profiler'); diff --git a/shells/browser/shared/src/panels/settings.js b/shells/browser/shared/src/panels/settings.js deleted file mode 100644 index a8dd78b050..0000000000 --- a/shells/browser/shared/src/panels/settings.js +++ /dev/null @@ -1,3 +0,0 @@ -import { createPanel } from './utils'; - -createPanel('settings'); diff --git a/shells/browser/shared/src/panels/utils.js b/shells/browser/shared/src/panels/utils.js deleted file mode 100644 index 1f807fc922..0000000000 --- a/shells/browser/shared/src/panels/utils.js +++ /dev/null @@ -1,72 +0,0 @@ -/* global chrome */ - -import { createElement } from 'react'; -import { unstable_createRoot as createRoot, flushSync } from 'react-dom'; -import DevTools from 'src/devtools/views/DevTools'; -import { getBrowserName, getBrowserTheme } from '../utils'; - -export function createPanel(defaultTab) { - let injectedBridge = null; - let injectedStore = null; - let root = null; - - // All DevTools panel share a single Bridge and Store instance. - // The main script will inject those shared instances using this method. - window.injectBridgeAndStore = (bridge, store) => { - injectedBridge = bridge; - injectedStore = store; - - if (root === null) { - injectAndInit(); - } else { - // It's easiest to recreate the DevTools panel (to clean up potential stale state). - // We can revisit this in the future as a small optimization. - flushSync(() => root.unmount(injectAndInit)); - } - }; - - function viewElementSource(id) { - if (injectedBridge == null || injectedStore == null) { - return; - } - - const rendererID = injectedStore.getRendererIDForElement(id); - if (rendererID != null) { - // Ask the renderer interface to determine the component function, - // and store it as a global variable on the window - injectedBridge.send('viewElementSource', { id, rendererID }); - - setTimeout(() => { - // Ask Chrome to display the location of the component function, - // assuming the renderer found one. - chrome.devtools.inspectedWindow.eval(` - if (window.$type != null) { - inspect(window.$type); - } - `); - }, 100); - } - } - - function injectAndInit() { - const container = ((document.getElementById( - 'container' - ): any): HTMLElement); - - // Clear the "React not found" initial message before rendering. - container.innerHTML = ''; - - root = createRoot(container); - root.render( - createElement(DevTools, { - bridge: injectedBridge, - browserName: getBrowserName(), - browserTheme: getBrowserTheme(), - defaultTab, - showTabBar: false, - store: injectedStore, - viewElementSource, - }) - ); - } -} diff --git a/shells/browser/shared/src/utils.js b/shells/browser/shared/src/utils.js index 7f4c2eaa06..0446ff9daf 100644 --- a/shells/browser/shared/src/utils.js +++ b/shells/browser/shared/src/utils.js @@ -2,6 +2,27 @@ const IS_CHROME = navigator.userAgent.indexOf('Firefox') < 0; +export function createViewElementSource(bridge: Bridge, store: Store) { + return function viewElementSource(id) { + const rendererID = store.getRendererIDForElement(id); + if (rendererID != null) { + // Ask the renderer interface to determine the component function, + // and store it as a global variable on the window + bridge.send('viewElementSource', { id, rendererID }); + + setTimeout(() => { + // Ask Chrome to display the location of the component function, + // assuming the renderer found one. + chrome.devtools.inspectedWindow.eval(` + if (window.$type != null) { + inspect(window.$type); + } + `); + }, 100); + } + }; +} + export function getBrowserName() { return IS_CHROME ? 'Chrome' : 'Firefox'; } diff --git a/shells/browser/shared/webpack.config.js b/shells/browser/shared/webpack.config.js index e95c5cf418..44e3d5b5c2 100644 --- a/shells/browser/shared/webpack.config.js +++ b/shells/browser/shared/webpack.config.js @@ -16,9 +16,7 @@ module.exports = { contentScript: './src/contentScript.js', inject: './src/GlobalHook.js', main: './src/main.js', - elements: './src/panels/elements.js', - profiler: './src/panels/profiler.js', - settings: './src/panels/settings.js', + panel: './src/panel.js', }, output: { path: __dirname + '/build', diff --git a/src/devtools/views/Button.css b/src/devtools/views/Button.css index 31291d7f3b..a37af0ee30 100644 --- a/src/devtools/views/Button.css +++ b/src/devtools/views/Button.css @@ -22,7 +22,8 @@ outline: none; box-shadow: 0 0 0 2px var(--color-button-background-focus) inset; } -.Button:disabled { +.Button:disabled, +.Button:disabled:active { background: var(--color-button-background); color: var(--color-button-disabled); cursor: default; diff --git a/src/devtools/views/DevTools.js b/src/devtools/views/DevTools.js index 3473582f04..56eee3b56e 100644 --- a/src/devtools/views/DevTools.js +++ b/src/devtools/views/DevTools.js @@ -25,11 +25,24 @@ export type TabID = 'elements' | 'profiler' | 'settings'; export type Props = {| bridge: Bridge, browserName: BrowserName, - defaultTab?: TabID, browserTheme: BrowserTheme, + defaultTab?: TabID, showTabBar?: boolean, store: Store, viewElementSource?: ?Function, + + // This property is used only by the web extension target. + // The built-in tab UI is hidden in that case, in favor of the browser's own panel tabs. + // This is done to save space within the app. + // Because of this, the extension needs to be able to change which tab is active/rendered. + overrideTab?: TabID, + + // To avoid potential multi-root trickiness, the web extension uses portals to render tabs. + // The root app is rendered in the top-level extension window, + // but individual tabs (e.g. Elements, Profiling) can be rendered into portals within their browser panels. + elementsPortalContainer?: Element, + profilerPortalContainer?: Element, + settingsPortalContainer?: Element, |}; const elementTab = { @@ -57,13 +70,21 @@ const tabsWithoutProfiler = [elementTab, settingsTab]; export default function DevTools({ bridge, browserName, - defaultTab = 'elements', browserTheme = 'light', + defaultTab = 'elements', + elementsPortalContainer, + overrideTab, + profilerPortalContainer, + settingsPortalContainer, showTabBar = false, store, viewElementSource = null, }: Props) { const [tab, setTab] = useState(defaultTab); + if (overrideTab != null && overrideTab !== tab) { + setTab(overrideTab); + } + const [supportsProfiling, setSupportsProfiling] = useState( store.supportsProfiling ); @@ -89,21 +110,31 @@ export default function DevTools({ let tabElement; switch (tab) { case 'profiler': - tabElement = ; + tabElement = ( + + ); break; case 'settings': - tabElement = ; + tabElement = ; break; case 'elements': default: - tabElement = ; + tabElement = ; break; } return ( - +
diff --git a/src/devtools/views/Elements/Elements.js b/src/devtools/views/Elements/Elements.js index cb401ddfdb..596a5c754a 100644 --- a/src/devtools/views/Elements/Elements.js +++ b/src/devtools/views/Elements/Elements.js @@ -1,15 +1,18 @@ // @flow import React from 'react'; +import { createPortal } from 'react-dom'; import Tree from './Tree'; import SelectedElement from './SelectedElement'; import styles from './Elements.css'; -export type Props = {||}; +export type Props = {| + portalContainer?: Element, +|}; -export default function Elements(_: Props) { +export default function Elements({ portalContainer }: Props) { // TODO Flex wrappers below should be user resizable. - return ( + const children = (
@@ -19,4 +22,8 @@ export default function Elements(_: Props) {
); + + return portalContainer != null + ? createPortal(children, portalContainer) + : children; } diff --git a/src/devtools/views/Profiler/CommitRanked.js b/src/devtools/views/Profiler/CommitRanked.js index 1fb4ded3ee..acdd0cb884 100644 --- a/src/devtools/views/Profiler/CommitRanked.js +++ b/src/devtools/views/Profiler/CommitRanked.js @@ -104,7 +104,7 @@ function CommitRanked({ height, width }: {| height: number, width: number |}) { return ( ); } else { - return ; + children = ; } + + return portalContainer != null + ? createPortal(children, portalContainer) + : children; } // This view is rendered when there is no profiler data (either we haven't profiled yet or we're currently profiling). @@ -37,12 +52,16 @@ export default function Profiler(_: {||}) { function NonSuspendingProfiler({ hasProfilingData, isProfiling, + supportsProfiling, }: {| hasProfilingData: boolean, isProfiling: boolean, + supportsProfiling: boolean, |}) { let view = null; - if (isProfiling) { + if (!supportsProfiling) { + view = ; + } else if (isProfiling) { view = ; } else if (!hasProfilingData) { view = ; @@ -54,7 +73,7 @@ function NonSuspendingProfiler({
- +
); +const ProfilingNotSupported = () => ( +
+
Profiling not supported.
+
+

+ Profiling support requires either a development or production-profiling + build of React v16.5+. +

+

+ Learn more at{' '} + + fb.me/react-profiling + + . +

+
+
+); + const RecortdingInProgress = () => (
Profiling is in progress...
diff --git a/src/devtools/views/Profiler/RecordToggle.js b/src/devtools/views/Profiler/RecordToggle.js index 4d1bb591d9..94074002ca 100644 --- a/src/devtools/views/Profiler/RecordToggle.js +++ b/src/devtools/views/Profiler/RecordToggle.js @@ -7,9 +7,11 @@ import { ProfilerContext } from './ProfilerContext'; import styles from './RecordToggle.css'; -export type Props = {||}; +export type Props = {| + disabled?: boolean, +|}; -export default function RecordToggle(_: Props) { +export default function RecordToggle({ disabled }: Props) { const { isProfiling, startProfiling, stopProfiling } = useContext( ProfilerContext ); @@ -19,6 +21,7 @@ export default function RecordToggle(_: Props) { className={ isProfiling ? styles.ActiveRecordToggle : styles.InactiveRecordToggle } + disabled={disabled} onClick={isProfiling ? stopProfiling : startProfiling} title={isProfiling ? 'Stop profiling' : 'Start profiling'} > diff --git a/src/devtools/views/Settings/Settings.js b/src/devtools/views/Settings/Settings.js index 37e4dd9198..d3a99360a0 100644 --- a/src/devtools/views/Settings/Settings.js +++ b/src/devtools/views/Settings/Settings.js @@ -1,13 +1,16 @@ // @flow import React, { useCallback, useContext } from 'react'; +import { createPortal } from 'react-dom'; import { SettingsContext } from './SettingsContext'; import styles from './Settings.css'; -export type Props = {||}; +export type Props = {| + portalContainer?: Element, +|}; -export default function Settings(_: Props) { +export default function Settings({ portalContainer }: Props) { const { displayDensity, setDisplayDensity, theme, setTheme } = useContext( SettingsContext ); @@ -26,7 +29,7 @@ export default function Settings(_: Props) { [setTheme] ); - return ( + const children = (
Theme
@@ -90,4 +93,8 @@ export default function Settings(_: Props) {
); + + return portalContainer != null + ? createPortal(children, portalContainer) + : children; } diff --git a/src/devtools/views/Settings/SettingsContext.js b/src/devtools/views/Settings/SettingsContext.js index 0c5458cbad..f04af61f32 100644 --- a/src/devtools/views/Settings/SettingsContext.js +++ b/src/devtools/views/Settings/SettingsContext.js @@ -23,18 +23,58 @@ type Context = {| const SettingsContext = createContext(((null: any): Context)); SettingsContext.displayName = 'SettingsContext'; +type DocumentElements = Array; + type Props = {| browserTheme: BrowserTheme, children: React$Node, + elementsPortalContainer?: Element, + profilerPortalContainer?: Element, + settingsPortalContainer?: Element, |}; -function SettingsContextController({ browserTheme, children }: Props) { +function SettingsContextController({ + browserTheme, + children, + elementsPortalContainer, + profilerPortalContainer, + settingsPortalContainer, +}: Props) { const [displayDensity, setDisplayDensity] = useLocalStorage( 'displayDensity', 'compact' ); const [theme, setTheme] = useLocalStorage('theme', 'auto'); + const documentElements = useMemo(() => { + const array: Array = [ + ((document.documentElement: any): HTMLElement), + ]; + if (elementsPortalContainer != null) { + array.push( + ((elementsPortalContainer.ownerDocument + .documentElement: any): HTMLElement) + ); + } + if (profilerPortalContainer != null) { + array.push( + ((profilerPortalContainer.ownerDocument + .documentElement: any): HTMLElement) + ); + } + if (settingsPortalContainer != null) { + array.push( + ((settingsPortalContainer.ownerDocument + .documentElement: any): HTMLElement) + ); + } + return array; + }, [ + elementsPortalContainer, + profilerPortalContainer, + settingsPortalContainer, + ]); + const comfortableLineHeight = parseInt( getComputedStyle((document.body: any)).getPropertyValue( '--comfortable-line-height-data' @@ -51,31 +91,31 @@ function SettingsContextController({ browserTheme, children }: Props) { useLayoutEffect(() => { switch (displayDensity) { case 'compact': - updateDisplayDensity('compact'); + updateDisplayDensity('compact', documentElements); break; case 'comfortable': - updateDisplayDensity('comfortable'); + updateDisplayDensity('comfortable', documentElements); break; default: throw Error(`Unsupported displayDensity value "${displayDensity}"`); } - }, [displayDensity]); + }, [displayDensity, documentElements]); useLayoutEffect(() => { switch (theme) { case 'light': - updateThemeVariables('light'); + updateThemeVariables('light', documentElements); break; case 'dark': - updateThemeVariables('dark'); + updateThemeVariables('dark', documentElements); break; case 'auto': - updateThemeVariables(browserTheme); + updateThemeVariables(browserTheme, documentElements); break; default: throw Error(`Unsupported theme value "${theme}"`); } - }, [browserTheme, theme]); + }, [browserTheme, theme, documentElements]); const value = useMemo( () => ({ @@ -105,63 +145,95 @@ function SettingsContextController({ browserTheme, children }: Props) { ); } -function setStyleVariable(name: string, value: string) { - (document.documentElement: any).style.setProperty(name, value); +function setStyleVariable( + name: string, + value: string, + documentElements: DocumentElements +) { + documentElements.forEach(documentElement => + documentElement.style.setProperty(name, value) + ); } -function updateStyleHelper(themeKey: string, style: string) { - setStyleVariable(`--${style}`, `var(--${themeKey}-${style})`); +function updateStyleHelper( + themeKey: string, + style: string, + documentElements: DocumentElements +) { + setStyleVariable( + `--${style}`, + `var(--${themeKey}-${style})`, + documentElements + ); } -function updateDisplayDensity(displayDensity: DisplayDensity): void { - updateStyleHelper(displayDensity, 'font-size-monospace-normal'); - updateStyleHelper(displayDensity, 'font-size-monospace-large'); - updateStyleHelper(displayDensity, 'font-size-sans-normal'); - updateStyleHelper(displayDensity, 'font-size-sans-large'); - updateStyleHelper(displayDensity, 'line-height-data'); +function updateDisplayDensity( + displayDensity: DisplayDensity, + documentElements: DocumentElements +): void { + updateStyleHelper( + displayDensity, + 'font-size-monospace-normal', + documentElements + ); + updateStyleHelper( + displayDensity, + 'font-size-monospace-large', + documentElements + ); + updateStyleHelper(displayDensity, 'font-size-sans-normal', documentElements); + updateStyleHelper(displayDensity, 'font-size-sans-large', documentElements); + updateStyleHelper(displayDensity, 'line-height-data', documentElements); } -function updateThemeVariables(theme: Theme): void { - updateStyleHelper(theme, 'color-attribute-name'); - updateStyleHelper(theme, 'color-attribute-value'); - updateStyleHelper(theme, 'color-attribute-editable-value'); - updateStyleHelper(theme, 'color-background'); - updateStyleHelper(theme, 'color-border'); - updateStyleHelper(theme, 'color-button-background'); - updateStyleHelper(theme, 'color-button-background-focus'); - updateStyleHelper(theme, 'color-button-background-hover'); - updateStyleHelper(theme, 'color-button'); - updateStyleHelper(theme, 'color-button-disabled'); - updateStyleHelper(theme, 'color-button-focus'); - updateStyleHelper(theme, 'color-button-hover'); - updateStyleHelper(theme, 'color-commit-did-not-render'); - updateStyleHelper(theme, 'color-commit-gradient-0'); - updateStyleHelper(theme, 'color-commit-gradient-1'); - updateStyleHelper(theme, 'color-commit-gradient-2'); - updateStyleHelper(theme, 'color-commit-gradient-3'); - updateStyleHelper(theme, 'color-commit-gradient-4'); - updateStyleHelper(theme, 'color-commit-gradient-5'); - updateStyleHelper(theme, 'color-commit-gradient-6'); - updateStyleHelper(theme, 'color-commit-gradient-7'); - updateStyleHelper(theme, 'color-commit-gradient-8'); - updateStyleHelper(theme, 'color-commit-gradient-9'); - updateStyleHelper(theme, 'color-commit-gradient-text'); - updateStyleHelper(theme, 'color-component-name'); - updateStyleHelper(theme, 'color-component-name-inverted'); - updateStyleHelper(theme, 'color-dim'); - updateStyleHelper(theme, 'color-dimmer'); - updateStyleHelper(theme, 'color-dimmest'); - updateStyleHelper(theme, 'color-jsx-arrow-brackets'); - updateStyleHelper(theme, 'color-jsx-arrow-brackets-inverted'); - updateStyleHelper(theme, 'color-modal-background'); - updateStyleHelper(theme, 'color-record-active'); - updateStyleHelper(theme, 'color-record-hover'); - updateStyleHelper(theme, 'color-record-inactive'); - updateStyleHelper(theme, 'color-tree-node-selected'); - updateStyleHelper(theme, 'color-tree-node-hover'); - updateStyleHelper(theme, 'color-search-match'); - updateStyleHelper(theme, 'color-search-match-current'); - updateStyleHelper(theme, 'color-text-color'); +function updateThemeVariables( + theme: Theme, + documentElements: DocumentElements +): void { + updateStyleHelper(theme, 'color-attribute-name', documentElements); + updateStyleHelper(theme, 'color-attribute-value', documentElements); + updateStyleHelper(theme, 'color-attribute-editable-value', documentElements); + updateStyleHelper(theme, 'color-background', documentElements); + updateStyleHelper(theme, 'color-border', documentElements); + updateStyleHelper(theme, 'color-button-background', documentElements); + updateStyleHelper(theme, 'color-button-background-focus', documentElements); + updateStyleHelper(theme, 'color-button-background-hover', documentElements); + updateStyleHelper(theme, 'color-button', documentElements); + updateStyleHelper(theme, 'color-button-disabled', documentElements); + updateStyleHelper(theme, 'color-button-focus', documentElements); + updateStyleHelper(theme, 'color-button-hover', documentElements); + updateStyleHelper(theme, 'color-commit-did-not-render', documentElements); + updateStyleHelper(theme, 'color-commit-gradient-0', documentElements); + updateStyleHelper(theme, 'color-commit-gradient-1', documentElements); + updateStyleHelper(theme, 'color-commit-gradient-2', documentElements); + updateStyleHelper(theme, 'color-commit-gradient-3', documentElements); + updateStyleHelper(theme, 'color-commit-gradient-4', documentElements); + updateStyleHelper(theme, 'color-commit-gradient-5', documentElements); + updateStyleHelper(theme, 'color-commit-gradient-6', documentElements); + updateStyleHelper(theme, 'color-commit-gradient-7', documentElements); + updateStyleHelper(theme, 'color-commit-gradient-8', documentElements); + updateStyleHelper(theme, 'color-commit-gradient-9', documentElements); + updateStyleHelper(theme, 'color-commit-gradient-text', documentElements); + updateStyleHelper(theme, 'color-component-name', documentElements); + updateStyleHelper(theme, 'color-component-name-inverted', documentElements); + updateStyleHelper(theme, 'color-dim', documentElements); + updateStyleHelper(theme, 'color-dimmer', documentElements); + updateStyleHelper(theme, 'color-dimmest', documentElements); + updateStyleHelper(theme, 'color-jsx-arrow-brackets', documentElements); + updateStyleHelper( + theme, + 'color-jsx-arrow-brackets-inverted', + documentElements + ); + updateStyleHelper(theme, 'color-modal-background', documentElements); + updateStyleHelper(theme, 'color-record-active', documentElements); + updateStyleHelper(theme, 'color-record-hover', documentElements); + updateStyleHelper(theme, 'color-record-inactive', documentElements); + updateStyleHelper(theme, 'color-tree-node-selected', documentElements); + updateStyleHelper(theme, 'color-tree-node-hover', documentElements); + updateStyleHelper(theme, 'color-search-match', documentElements); + updateStyleHelper(theme, 'color-search-match-current', documentElements); + updateStyleHelper(theme, 'color-text-color', documentElements); } export { SettingsContext, SettingsContextController }; diff --git a/src/devtools/views/root.css b/src/devtools/views/root.css index 65eb630d24..a8d562ee16 100644 --- a/src/devtools/views/root.css +++ b/src/devtools/views/root.css @@ -16,7 +16,7 @@ --light-color-button-focus: #3578e5; --light-color-button-hover: #3578e5; --light-color-border: #eeeeee; - --light-color-commit-did-not-render: #777d88; + --light-color-commit-did-not-render: #cfd1d5; --light-color-commit-gradient-0: #37afa9; --light-color-commit-gradient-1: #63b19e; --light-color-commit-gradient-2: #80b393; @@ -58,7 +58,7 @@ --dark-color-button-focus: #a2e9fc; --dark-color-button-hover: #a2e9fc; --dark-color-border: #3d424a; - --dark-color-commit-did-not-render: #8f949d; + --dark-color-commit-did-not-render: #777d88; --dark-color-commit-gradient-0: #37afa9; --dark-color-commit-gradient-1: #63b19e; --dark-color-commit-gradient-2: #80b393;