Merge pull request #33 from bvaughn/portals

Experimenting with portals [WIP]
This commit is contained in:
Brian Vaughn
2019-03-18 13:37:56 -07:00
committed by GitHub
23 changed files with 381 additions and 312 deletions
+1 -7
View File
@@ -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"],
+1 -7
View File
@@ -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"],
+1 -8
View File
@@ -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
@@ -27,6 +27,6 @@
<body>
<!-- main react mount point -->
<div id="container">Unable to find React on the page.</div>
<script src="./build/elements.js"></script>
<script src="./build/panel.js"></script>
</body>
</html>
-32
View File
@@ -1,32 +0,0 @@
<!doctype html>
<html style="display: flex">
<head>
<meta charset="utf8">
<style>
html {
display: flex;
}
body {
margin: 0;
padding: 0;
flex: 1;
display: flex;
}
#container {
display: flex;
flex: 1;
width: 100%;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
</head>
<body>
<!-- main react mount point -->
<div id="container">Unable to find React on the page.</div>
<script src="./build/profiler.js"></script>
</body>
</html>
-32
View File
@@ -1,32 +0,0 @@
<!doctype html>
<html style="display: flex">
<head>
<meta charset="utf8">
<style>
html {
display: flex;
}
body {
margin: 0;
padding: 0;
flex: 1;
display: flex;
}
#container {
display: flex;
flex: 1;
width: 100%;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
</head>
<body>
<!-- main react mount point -->
<div id="container">Unable to find React on the page.</div>
<script src="./build/settings.js"></script>
</body>
</html>
+92 -59
View File
@@ -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));
});
}
);
+18
View File
@@ -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);
}
}
};
@@ -1,3 +0,0 @@
import { createPanel } from './utils';
createPanel('elements');
@@ -1,3 +0,0 @@
import { createPanel } from './utils';
createPanel('profiler');
@@ -1,3 +0,0 @@
import { createPanel } from './utils';
createPanel('settings');
-72
View File
@@ -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,
})
);
}
}
+21
View File
@@ -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';
}
+1 -3
View File
@@ -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',
+2 -1
View File
@@ -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;
+37 -6
View File
@@ -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 <DevTools> 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 = <Profiler />;
tabElement = (
<Profiler
portalContainer={profilerPortalContainer}
supportsProfiling={supportsProfiling}
/>
);
break;
case 'settings':
tabElement = <Settings />;
tabElement = <Settings portalContainer={settingsPortalContainer} />;
break;
case 'elements':
default:
tabElement = <Elements />;
tabElement = <Elements portalContainer={elementsPortalContainer} />;
break;
}
return (
<BridgeContext.Provider value={bridge}>
<StoreContext.Provider value={store}>
<SettingsContextController browserTheme={browserTheme}>
<SettingsContextController
browserTheme={browserTheme}
elementsPortalContainer={elementsPortalContainer}
profilerPortalContainer={profilerPortalContainer}
settingsPortalContainer={settingsPortalContainer}
>
<TreeContextController viewElementSource={viewElementSource}>
<ProfilerContextController>
<div className={styles.DevTools}>
+10 -3
View File
@@ -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 = (
<div className={styles.Elements}>
<div className={styles.TreeWrapper}>
<Tree />
@@ -19,4 +22,8 @@ export default function Elements(_: Props) {
</div>
</div>
);
return portalContainer != null
? createPortal(children, portalContainer)
: children;
}
+1 -1
View File
@@ -104,7 +104,7 @@ function CommitRanked({ height, width }: {| height: number, width: number |}) {
return (
<FixedSizeList
height={height}
innerTagName="svg"
innerElementType="svg"
itemCount={chartData.nodes.length}
itemData={itemData}
itemSize={barHeight}
+47 -5
View File
@@ -1,6 +1,7 @@
// @flow
import React, { Suspense, useCallback, useContext, useState } from 'react';
import { createPortal } from 'react-dom';
import { ProfilerContext } from './ProfilerContext';
import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
@@ -13,21 +14,35 @@ import SnapshotSelector from './SnapshotSelector';
import styles from './Profiler.css';
export default function Profiler(_: {||}) {
export type Props = {|
portalContainer?: Element,
supportsProfiling: boolean,
|};
export default function Profiler({
portalContainer,
supportsProfiling,
}: Props) {
const { hasProfilingData, isProfiling, rootHasProfilingData } = useContext(
ProfilerContext
);
let children = null;
if (isProfiling || !rootHasProfilingData) {
return (
children = (
<NonSuspendingProfiler
hasProfilingData={hasProfilingData}
isProfiling={isProfiling}
supportsProfiling={supportsProfiling}
/>
);
} else {
return <SuspendingProfiler />;
children = <SuspendingProfiler />;
}
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 = <ProfilingNotSupported />;
} else if (isProfiling) {
view = <RecortdingInProgress />;
} else if (!hasProfilingData) {
view = <NoProfilingData />;
@@ -54,7 +73,7 @@ function NonSuspendingProfiler({
<div className={styles.Profiler}>
<div className={styles.LeftColumn}>
<div className={styles.Toolbar}>
<RecordToggle />
<RecordToggle disabled={!supportsProfiling} />
<Button disabled title="Reload and start profiling">
{/* TODO (profiling) Wire up reload button */}
<ButtonIcon type="reload" />
@@ -190,6 +209,29 @@ const NoProfilingDataForRoot = () => (
</div>
);
const ProfilingNotSupported = () => (
<div className={styles.Column}>
<div className={styles.Header}>Profiling not supported.</div>
<div className={styles.Column}>
<p>
Profiling support requires either a development or production-profiling
build of React v16.5+.
</p>
<p>
Learn more at{' '}
<a
href="https://fb.me/react-profiling"
rel="noopener noreferrer"
target="_blank"
>
fb.me/react-profiling
</a>
.
</p>
</div>
</div>
);
const RecortdingInProgress = () => (
<div className={styles.Column}>
<div className={styles.Header}>Profiling is in progress...</div>
+5 -2
View File
@@ -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'}
>
+10 -3
View File
@@ -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 = (
<div className={styles.Settings}>
<div className={styles.Section}>
<div className={styles.Header}>Theme</div>
@@ -90,4 +93,8 @@ export default function Settings(_: Props) {
</div>
</div>
);
return portalContainer != null
? createPortal(children, portalContainer)
: children;
}
+131 -59
View File
@@ -23,18 +23,58 @@ type Context = {|
const SettingsContext = createContext<Context>(((null: any): Context));
SettingsContext.displayName = 'SettingsContext';
type DocumentElements = Array<HTMLElement>;
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>(
'displayDensity',
'compact'
);
const [theme, setTheme] = useLocalStorage<Theme>('theme', 'auto');
const documentElements = useMemo<DocumentElements>(() => {
const array: Array<HTMLElement> = [
((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 };
+2 -2
View File
@@ -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;