[DevTools] Allow all file links in Chrome DevTools to open in external editor (#33985)

Stacked on #33983.

Allow React to be configured as the default handler of all links in
Chrome DevTools. To do this you need to configure the Chrome DevTools
setting for "Link Handling:" to be set to "React Developer Tools". By
default this doesn't do anything but if you then check the box added in
#33983 it starts open local files directly in the external editor.

This needs docs to show how to enable that option.

(As far as I can tell this broke in Chrome Canary 🙄 but hopefully fixed
before stable.)
This commit is contained in:
Sebastian Markbåge 2025-07-25 10:27:09 -04:00 committed by GitHub
parent 190758e623
commit 36c2bf5c3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 94 additions and 7 deletions

View File

@ -18,7 +18,12 @@ import {
LOCAL_STORAGE_TRACE_UPDATES_ENABLED_KEY,
} from 'react-devtools-shared/src/constants';
import {logEvent} from 'react-devtools-shared/src/Logger';
import {normalizeUrlIfValid} from 'react-devtools-shared/src/utils';
import {
getAlwaysOpenInEditor,
getOpenInEditorURL,
normalizeUrlIfValid,
} from 'react-devtools-shared/src/utils';
import {checkConditions} from 'react-devtools-shared/src/devtools/views/Editor/utils';
import {
setBrowserSelectionFromReact,
@ -543,3 +548,32 @@ if (chrome.devtools.panels.setThemeChangeHandler) {
// Firefox
chrome.devtools.panels.onThemeChanged.addListener(onThemeChanged);
}
// Firefox doesn't support resources handlers yet.
if (chrome.devtools.panels.setOpenResourceHandler) {
chrome.devtools.panels.setOpenResourceHandler(
(
resource,
lineNumber = 1,
// The column is a new feature so we have to specify a default if it doesn't exist
columnNumber = 1,
) => {
const alwaysOpenInEditor = getAlwaysOpenInEditor();
const editorURL = getOpenInEditorURL();
if (alwaysOpenInEditor && editorURL) {
const location = ['', resource.url, lineNumber, columnNumber];
const {url, shouldDisableButton} = checkConditions(editorURL, location);
if (!shouldDisableButton) {
window.open(url);
return;
}
}
// Otherwise fallback to the built-in behavior.
chrome.devtools.panels.openResource(
resource.url,
lineNumber - 1,
columnNumber - 1,
);
},
);
}

View File

@ -37,6 +37,7 @@ export type Props = {selectedSource: ?SourceSelection};
function EditorPane({selectedSource}: Props) {
const [showSettings, setShowSettings] = useState(false);
const [showLinkInfo, setShowLinkInfo] = useState(false);
const editorURL = useSyncExternalStore(
function subscribe(callback) {
@ -50,6 +51,30 @@ function EditorPane({selectedSource}: Props) {
},
);
if (showLinkInfo) {
return (
<div className={styles.EditorPane}>
<div className={styles.EditorToolbar}>
<div style={{display: 'flex', flex: '1 1 auto'}}>
To enable link handling in your browser's DevTools settings, look
for the option Extension -> Link Handling. Select "React Developer
Tools".
</div>
<div className={styles.VRule} />
<Button
onClick={() =>
startTransition(() => {
setShowLinkInfo(false);
setShowSettings(false);
})
}>
<ButtonIcon type="close" />
</Button>
</div>
</div>
);
}
let editorToolbar;
if (showSettings) {
editorToolbar = (
@ -87,7 +112,13 @@ function EditorPane({selectedSource}: Props) {
{editorToolbar}
<div className={styles.EditorInfo}>
{editorURL ? (
<CodeEditorByDefault />
<CodeEditorByDefault
onChange={alwaysOpenInEditor => {
if (alwaysOpenInEditor) {
startTransition(() => setShowLinkInfo(true));
}
}}
/>
) : (
'Configure an external editor to open local files.'
)}

View File

@ -13,7 +13,11 @@ import {useLocalStorage} from '../hooks';
import styles from './SettingsShared.css';
export default function CodeEditorByDefault(_: {}): React.Node {
export default function CodeEditorByDefault({
onChange,
}: {
onChange?: boolean => void,
}): React.Node {
const [alwaysOpenInEditor, setAlwaysOpenInEditor] = useLocalStorage<boolean>(
LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR,
false,
@ -24,9 +28,12 @@ export default function CodeEditorByDefault(_: {}): React.Node {
<input
type="checkbox"
checked={alwaysOpenInEditor}
onChange={({currentTarget}) =>
setAlwaysOpenInEditor(currentTarget.checked)
}
onChange={({currentTarget}) => {
setAlwaysOpenInEditor(currentTarget.checked);
if (onChange) {
onChange(currentTarget.checked);
}
}}
className={styles.SettingRowCheckbox}
/>
Open local files directly in your code editor

View File

@ -13,10 +13,13 @@ import {SettingsContext} from './SettingsContext';
import {StoreContext} from '../context';
import {CHANGE_LOG_URL} from 'react-devtools-shared/src/devtools/constants';
import {isInternalFacebookBuild} from 'react-devtools-feature-flags';
import CodeEditorOptions from './CodeEditorOptions';
import styles from './SettingsShared.css';
import CodeEditorOptions from './CodeEditorOptions';
import CodeEditorByDefault from './CodeEditorByDefault';
import {LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR} from '../../../constants';
import {useLocalStorage} from '../hooks';
function getChangeLogUrl(version: ?string): string | null {
if (!version) {
@ -47,6 +50,11 @@ export default function GeneralSettings(_: {}): React.Node {
const showBackendVersion =
backendVersion && backendVersion !== frontendVersion;
const [alwaysOpenInEditor] = useLocalStorage<boolean>(
LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR,
false,
);
return (
<div className={styles.SettingList}>
{isInternalFacebookBuild && (
@ -87,6 +95,13 @@ export default function GeneralSettings(_: {}): React.Node {
<div className={styles.SettingWrapper}>
<CodeEditorByDefault />
{alwaysOpenInEditor && (__IS_CHROME__ || __IS_EDGE__) ? (
<div>
To enable link handling in your browser's DevTools settings, look
for the option Extension -> Link Handling. Select "React Developer
Tools".
</div>
) : null}
</div>
<div className={styles.SettingWrapper}>