From f005309ac82a04264302e20819ebe09766698f39 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Thu, 24 Jul 2025 14:09:56 -0400 Subject: [PATCH 1/7] Extract useOpenResource helper to open a source location generally --- .../views/Components/InspectedElement.js | 3 +- .../Components/InspectedElementSourcePanel.js | 22 ++----- .../InspectedElementViewSourceButton.js | 45 +++----------- .../views/Profiler/SidebarEventInfo.js | 18 ++---- .../src/devtools/views/useOpenResource.js | 60 +++++++++++++++++++ 5 files changed, 77 insertions(+), 71 deletions(-) create mode 100644 packages/react-devtools-shared/src/devtools/views/useOpenResource.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 8210e12331b..0daa7f51536 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElement.js @@ -271,8 +271,7 @@ export default function InspectedElementWrapper(_: Props): React.Node { {!hideViewSourceAction && ( )} diff --git a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.js b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.js index 91780cdc13d..585361cedcf 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.js @@ -8,7 +8,6 @@ */ import * as React from 'react'; -import {useCallback, useContext} from 'react'; import {copy} from 'clipboard-js'; import {toNormalUrl} from 'jsc-safe-url'; @@ -17,7 +16,7 @@ import ButtonIcon from '../ButtonIcon'; import Skeleton from './Skeleton'; import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck'; -import ViewElementSourceContext from './ViewElementSourceContext'; +import useOpenResource from '../useOpenResource'; import type {ReactFunctionLocation} from 'shared/ReactTypes'; import styles from './InspectedElementSourcePanel.css'; @@ -91,24 +90,11 @@ function CopySourceButton({source, symbolicatedSourcePromise}: Props) { function FormattedSourceString({source, symbolicatedSourcePromise}: Props) { const symbolicatedSource = React.use(symbolicatedSourcePromise); - const {canViewElementSourceFunction, viewElementSourceFunction} = useContext( - ViewElementSourceContext, + const [linkIsEnabled, viewSource] = useOpenResource( + source, + symbolicatedSource, ); - // In some cases (e.g. FB internal usage) the standalone shell might not be able to view the source. - // To detect this case, we defer to an injected helper function (if present). - const linkIsEnabled = - viewElementSourceFunction != null && - source != null && - (canViewElementSourceFunction == null || - canViewElementSourceFunction(source, symbolicatedSource)); - - const viewSource = useCallback(() => { - if (viewElementSourceFunction != null && source != null) { - viewElementSourceFunction(source, symbolicatedSource); - } - }, [source, symbolicatedSource]); - const [, sourceURL, line] = symbolicatedSource == null ? source : symbolicatedSource; diff --git a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementViewSourceButton.js b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementViewSourceButton.js index 6043bb61df9..23d4cf96c82 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementViewSourceButton.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/InspectedElementViewSourceButton.js @@ -11,79 +11,48 @@ import * as React from 'react'; import ButtonIcon from '../ButtonIcon'; import Button from '../Button'; -import ViewElementSourceContext from './ViewElementSourceContext'; import Skeleton from './Skeleton'; import type {ReactFunctionLocation} from 'shared/ReactTypes'; -import type { - CanViewElementSource, - ViewElementSource, -} from 'react-devtools-shared/src/devtools/views/DevTools'; -const {useCallback, useContext} = React; +import useOpenResource from '../useOpenResource'; type Props = { - canViewSource: ?boolean, - source: ?ReactFunctionLocation, + source: null | ReactFunctionLocation, symbolicatedSourcePromise: Promise | null, }; function InspectedElementViewSourceButton({ - canViewSource, source, symbolicatedSourcePromise, }: Props): React.Node { - const {canViewElementSourceFunction, viewElementSourceFunction} = useContext( - ViewElementSourceContext, - ); - return ( }> ); } type ActualSourceButtonProps = { - canViewSource: ?boolean, - source: ?ReactFunctionLocation, + source: null | ReactFunctionLocation, symbolicatedSourcePromise: Promise | null, - canViewElementSourceFunction: CanViewElementSource | null, - viewElementSourceFunction: ViewElementSource | null, }; function ActualSourceButton({ - canViewSource, source, symbolicatedSourcePromise, - canViewElementSourceFunction, - viewElementSourceFunction, }: ActualSourceButtonProps): React.Node { const symbolicatedSource = symbolicatedSourcePromise == null ? null : React.use(symbolicatedSourcePromise); - // In some cases (e.g. FB internal usage) the standalone shell might not be able to view the source. - // To detect this case, we defer to an injected helper function (if present). - const buttonIsEnabled = - !!canViewSource && - viewElementSourceFunction != null && - source != null && - (canViewElementSourceFunction == null || - canViewElementSourceFunction(source, symbolicatedSource)); - - const viewSource = useCallback(() => { - if (viewElementSourceFunction != null && source != null) { - viewElementSourceFunction(source, symbolicatedSource); - } - }, [source, symbolicatedSource]); - + const [buttonIsEnabled, viewSource] = useOpenResource( + source, + symbolicatedSource, + ); return ( ); + } else { + editorToolbar = ( +
+ +
+ +
+ ); } return (
- -
- + {editorToolbar} +
+ {editorURL ? ( + + ) : ( + 'Configure an external editor to open local files.' + )} +
); } diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorByDefault.js b/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorByDefault.js new file mode 100644 index 00000000000..74dba4947b2 --- /dev/null +++ b/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorByDefault.js @@ -0,0 +1,35 @@ +/** + * 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 * as React from 'react'; +import {LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR} from '../../../constants'; +import {useLocalStorage} from '../hooks'; + +import styles from './SettingsShared.css'; + +export default function CodeEditorByDefault(_: {}): React.Node { + const [alwaysOpenInEditor, setAlwaysOpenInEditor] = useLocalStorage( + LOCAL_STORAGE_ALWAYS_OPEN_IN_EDITOR, + false, + ); + + return ( + + ); +} 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 0eb489611b4..3c9a24ef8a5 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorOptions.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/CodeEditorOptions.js @@ -19,7 +19,7 @@ import styles from './SettingsShared.css'; const vscodeFilepath = 'vscode://file/{path}:{line}'; -export default function ComponentsSettings({ +export default function CodeEditorOptions({ environmentNames, }: { environmentNames: Promise>, diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js b/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js index 8246bc85939..c6db4e89320 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js @@ -16,6 +16,7 @@ import {isInternalFacebookBuild} from 'react-devtools-feature-flags'; import CodeEditorOptions from './CodeEditorOptions'; import styles from './SettingsShared.css'; +import CodeEditorByDefault from './CodeEditorByDefault'; function getChangeLogUrl(version: ?string): string | null { if (!version) { @@ -84,6 +85,22 @@ export default function GeneralSettings(_: {}): React.Node {
+
+ +
+ +
+
Display density
+ +
+ {supportsTraceUpdates && (