From 3a8c1fb2a01ae1af09a6a5248b5dcae043aed55b Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Thu, 24 Jul 2025 20:52:09 -0400 Subject: [PATCH 1/3] Extract useEditorURL helper hook to read the editor url --- .../views/Components/InspectedElement.js | 15 +++------- .../src/devtools/views/Editor/EditorPane.js | 17 ++--------- .../src/devtools/views/useEditorURL.js | 28 +++++++++++++++++++ .../src/devtools/views/useOpenResource.js | 18 +++--------- 4 files changed, 39 insertions(+), 39 deletions(-) create mode 100644 packages/react-devtools-shared/src/devtools/views/useEditorURL.js diff --git a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js index 0c980f0db48..cc37953f4d2 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js @@ -18,16 +18,14 @@ import Toggle from '../Toggle'; import {ElementTypeSuspense} from 'react-devtools-shared/src/frontend/types'; import InspectedElementView from './InspectedElementView'; import {InspectedElementContext} from './InspectedElementContext'; -import {getOpenInEditorURL, getAlwaysOpenInEditor} from '../../../utils'; -import { - LOCAL_STORAGE_OPEN_IN_EDITOR_URL, - LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR, -} from '../../../constants'; +import {getAlwaysOpenInEditor} from '../../../utils'; +import {LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR} from '../../../constants'; import FetchFileWithCachingContext from './FetchFileWithCachingContext'; import {symbolicateSourceWithCache} from 'react-devtools-shared/src/symbolicateSource'; import OpenInEditorButton from './OpenInEditorButton'; import InspectedElementViewSourceButton from './InspectedElementViewSourceButton'; import Skeleton from './Skeleton'; +import useEditorURL from '../useEditorURL'; import styles from './InspectedElement.css'; @@ -134,12 +132,7 @@ export default function InspectedElementWrapper(_: Props): React.Node { getAlwaysOpenInEditor, ); - const editorURL = useSyncExternalStore(function subscribe(callback) { - window.addEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback); - return function unsubscribe() { - window.removeEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback); - }; - }, getOpenInEditorURL); + const editorURL = useEditorURL(); const toggleErrored = useCallback(() => { if (inspectedElement == null) { diff --git a/packages/react-devtools-shared/src/devtools/views/Editor/EditorPane.js b/packages/react-devtools-shared/src/devtools/views/Editor/EditorPane.js index 09c76e1d26e..bc1187d8f6e 100644 --- a/packages/react-devtools-shared/src/devtools/views/Editor/EditorPane.js +++ b/packages/react-devtools-shared/src/devtools/views/Editor/EditorPane.js @@ -8,7 +8,7 @@ */ import * as React from 'react'; -import {useSyncExternalStore, useState, startTransition} from 'react'; +import {useState, startTransition} from 'react'; import portaledContent from '../portaledContent'; @@ -18,8 +18,7 @@ import Button from 'react-devtools-shared/src/devtools/views/Button'; import ButtonIcon from 'react-devtools-shared/src/devtools/views/ButtonIcon'; import OpenInEditorButton from './OpenInEditorButton'; -import {getOpenInEditorURL} from '../../../utils'; -import {LOCAL_STORAGE_OPEN_IN_EDITOR_URL} from '../../../constants'; +import useEditorURL from '../useEditorURL'; import EditorSettings from './EditorSettings'; import CodeEditorByDefault from '../Settings/CodeEditorByDefault'; @@ -38,17 +37,7 @@ export type Props = {selectedSource: ?SourceSelection}; function EditorPane({selectedSource}: Props) { const [showSettings, setShowSettings] = useState(false); - const editorURL = useSyncExternalStore( - function subscribe(callback) { - window.addEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback); - return function unsubscribe() { - window.removeEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback); - }; - }, - function getState() { - return getOpenInEditorURL(); - }, - ); + const editorURL = useEditorURL(); let editorToolbar; if (showSettings) { diff --git a/packages/react-devtools-shared/src/devtools/views/useEditorURL.js b/packages/react-devtools-shared/src/devtools/views/useEditorURL.js new file mode 100644 index 00000000000..2464bf28887 --- /dev/null +++ b/packages/react-devtools-shared/src/devtools/views/useEditorURL.js @@ -0,0 +1,28 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import {useCallback, useSyncExternalStore} from 'react'; + +import {getOpenInEditorURL} from '../../utils'; +import {LOCAL_STORAGE_OPEN_IN_EDITOR_URL} from '../../constants'; + +const useEditorURL = (): string => { + const editorURL = useSyncExternalStore( + useCallback(function subscribe(callback) { + window.addEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback); + return function unsubscribe() { + window.removeEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback); + }; + }, []), + getOpenInEditorURL, + ); + return editorURL; +}; + +export default useEditorURL; diff --git a/packages/react-devtools-shared/src/devtools/views/useOpenResource.js b/packages/react-devtools-shared/src/devtools/views/useOpenResource.js index c645efc1e5e..3a7fee5b684 100644 --- a/packages/react-devtools-shared/src/devtools/views/useOpenResource.js +++ b/packages/react-devtools-shared/src/devtools/views/useOpenResource.js @@ -13,11 +13,9 @@ import {useCallback, useContext, useSyncExternalStore} from 'react'; import ViewElementSourceContext from './Components/ViewElementSourceContext'; -import {getOpenInEditorURL, getAlwaysOpenInEditor} from '../../utils'; -import { - LOCAL_STORAGE_OPEN_IN_EDITOR_URL, - LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR, -} from '../../constants'; +import {getAlwaysOpenInEditor} from '../../utils'; +import useEditorURL from './useEditorURL'; +import {LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR} from '../../constants'; import {checkConditions} from './Editor/utils'; @@ -32,15 +30,7 @@ const useOpenResource = ( ViewElementSourceContext, ); - const editorURL = useSyncExternalStore( - useCallback(function subscribe(callback) { - window.addEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback); - return function unsubscribe() { - window.removeEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback); - }; - }, []), - getOpenInEditorURL, - ); + const editorURL = useEditorURL(); const alwaysOpenInEditor = useSyncExternalStore( useCallback(function subscribe(callback) { From 64f7b5710292fa4f1c80d394935e3a9b3a4d7cfe Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Thu, 24 Jul 2025 21:02:01 -0400 Subject: [PATCH 2/3] Get the URL of the preset when one is defined instead of the stored url That way we can update the preset. This also makes it so that you don't lose the custom url if you switch between a preset and custom. --- .../devtools/views/Settings/CodeEditorOptions.js | 7 ------- .../src/devtools/views/useEditorURL.js | 13 ++++++++++++- packages/react-devtools-shared/src/utils.js | 10 ++++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorOptions.js b/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorOptions.js index aa5a7767867..c7f4f10b778 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorOptions.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorOptions.js @@ -17,8 +17,6 @@ import {getDefaultOpenInEditorURL} from 'react-devtools-shared/src/utils'; import styles from './SettingsShared.css'; -const vscodeFilepath = 'vscode://file/{path}:{line}:{column}'; - export default function CodeEditorOptions({ environmentNames, }: { @@ -40,11 +38,6 @@ export default function CodeEditorOptions({ onChange={({currentTarget}) => { const selectedValue = currentTarget.value; setOpenInEditorURLPreset(selectedValue); - if (selectedValue === 'vscode') { - setOpenInEditorURL(vscodeFilepath); - } else if (selectedValue === 'custom') { - setOpenInEditorURL(''); - } }}> diff --git a/packages/react-devtools-shared/src/devtools/views/useEditorURL.js b/packages/react-devtools-shared/src/devtools/views/useEditorURL.js index 2464bf28887..303758d6968 100644 --- a/packages/react-devtools-shared/src/devtools/views/useEditorURL.js +++ b/packages/react-devtools-shared/src/devtools/views/useEditorURL.js @@ -10,14 +10,25 @@ import {useCallback, useSyncExternalStore} from 'react'; import {getOpenInEditorURL} from '../../utils'; -import {LOCAL_STORAGE_OPEN_IN_EDITOR_URL} from '../../constants'; +import { + LOCAL_STORAGE_OPEN_IN_EDITOR_URL, + LOCAL_STORAGE_OPEN_IN_EDITOR_URL_PRESET, +} from '../../constants'; const useEditorURL = (): string => { const editorURL = useSyncExternalStore( useCallback(function subscribe(callback) { window.addEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback); + window.addEventListener( + LOCAL_STORAGE_OPEN_IN_EDITOR_URL_PRESET, + callback, + ); return function unsubscribe() { window.removeEventListener(LOCAL_STORAGE_OPEN_IN_EDITOR_URL, callback); + window.removeEventListener( + LOCAL_STORAGE_OPEN_IN_EDITOR_URL_PRESET, + callback, + ); }; }, []), getOpenInEditorURL, diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index eb4044909eb..1ff7b859444 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -35,6 +35,7 @@ import { TREE_OPERATION_UPDATE_TREE_BASE_DURATION, LOCAL_STORAGE_COMPONENT_FILTER_PREFERENCES_KEY, LOCAL_STORAGE_OPEN_IN_EDITOR_URL, + LOCAL_STORAGE_OPEN_IN_EDITOR_URL_PRESET, LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR, SESSION_STORAGE_RELOAD_AND_PROFILE_KEY, SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY, @@ -385,6 +386,8 @@ export function filterOutLocationComponentFilters( return componentFilters.filter(f => f.type !== ComponentFilterLocation); } +const vscodeFilepath = 'vscode://file/{path}:{line}:{column}'; + export function getDefaultOpenInEditorURL(): string { return typeof process.env.EDITOR_URL === 'string' ? process.env.EDITOR_URL @@ -393,6 +396,13 @@ export function getDefaultOpenInEditorURL(): string { export function getOpenInEditorURL(): string { try { + const rawPreset = localStorageGetItem( + LOCAL_STORAGE_OPEN_IN_EDITOR_URL_PRESET, + ); + switch (rawPreset) { + case '"vscode"': + return vscodeFilepath; + } const raw = localStorageGetItem(LOCAL_STORAGE_OPEN_IN_EDITOR_URL); if (raw != null) { return JSON.parse(raw); From 25212947ff64fb124fbb13e186fa83ea1ae97b2c Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Thu, 24 Jul 2025 21:18:41 -0400 Subject: [PATCH 3/3] Default to vscode if no option was configured --- .../src/devtools/views/Settings/CodeEditorOptions.js | 9 ++++++--- packages/react-devtools-shared/src/utils.js | 6 +++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorOptions.js b/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorOptions.js index c7f4f10b778..d6e81ef3554 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorOptions.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorOptions.js @@ -13,7 +13,10 @@ import { LOCAL_STORAGE_OPEN_IN_EDITOR_URL_PRESET, } from '../../../constants'; import {useLocalStorage} from '../hooks'; -import {getDefaultOpenInEditorURL} from 'react-devtools-shared/src/utils'; +import { + getDefaultPreset, + getDefaultOpenInEditorURL, +} from 'react-devtools-shared/src/utils'; import styles from './SettingsShared.css'; @@ -24,7 +27,7 @@ export default function CodeEditorOptions({ }): React.Node { const [openInEditorURLPreset, setOpenInEditorURLPreset] = useLocalStorage< 'vscode' | 'custom', - >(LOCAL_STORAGE_OPEN_IN_EDITOR_URL_PRESET, 'custom'); + >(LOCAL_STORAGE_OPEN_IN_EDITOR_URL_PRESET, getDefaultPreset()); const [openInEditorURL, setOpenInEditorURL] = useLocalStorage( LOCAL_STORAGE_OPEN_IN_EDITOR_URL, @@ -46,7 +49,7 @@ export default function CodeEditorOptions({ { setOpenInEditorURL(event.target.value); diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index 1ff7b859444..325224844d7 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -388,10 +388,14 @@ export function filterOutLocationComponentFilters( const vscodeFilepath = 'vscode://file/{path}:{line}:{column}'; +export function getDefaultPreset(): 'custom' | 'vscode' { + return typeof process.env.EDITOR_URL === 'string' ? 'custom' : 'vscode'; +} + export function getDefaultOpenInEditorURL(): string { return typeof process.env.EDITOR_URL === 'string' ? process.env.EDITOR_URL - : ''; + : vscodeFilepath; } export function getOpenInEditorURL(): string {