[DevTools] Linkify Source View (#33954)

This makes it so you can click the source location itself to view the
source. This is similar styling as the link to jump to function props
like events and actions. We're going to need a lot more linkifying to
jump to various source locations. Also, I always was trying to click
this file anyway.

Hover state:

<img width="485" height="382" alt="Screenshot 2025-07-21 at 4 36 10 PM"
src="https://github.com/user-attachments/assets/1f0f8f8c-6866-4e62-ab84-1fb5ba012986"
/>
This commit is contained in:
Sebastian Markbåge
2025-07-21 17:36:37 -04:00
committed by GitHub
parent 074e92777c
commit bb4418d647
2 changed files with 50 additions and 12 deletions
@@ -18,3 +18,18 @@
max-width: 100%;
margin-left: 1rem;
}
.Link {
color: var(--color-link);
white-space: pre;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
cursor: pointer;
border-radius: 0.125rem;
padding: 0px 2px;
}
.Link:hover {
background-color: var(--color-background-hover);
}
@@ -8,6 +8,7 @@
*/
import * as React from 'react';
import {useCallback, useContext} from 'react';
import {copy} from 'clipboard-js';
import {toNormalUrl} from 'jsc-safe-url';
@@ -16,6 +17,8 @@ import ButtonIcon from '../ButtonIcon';
import Skeleton from './Skeleton';
import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck';
import ViewElementSourceContext from './ViewElementSourceContext';
import type {Source as InspectedElementSource} from 'react-devtools-shared/src/shared/types';
import styles from './InspectedElementSourcePanel.css';
@@ -87,25 +90,45 @@ function CopySourceButton({source, symbolicatedSourcePromise}: Props) {
function FormattedSourceString({source, symbolicatedSourcePromise}: Props) {
const symbolicatedSource = React.use(symbolicatedSourcePromise);
const {canViewElementSourceFunction, viewElementSourceFunction} = useContext(
ViewElementSourceContext,
);
// 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]);
let sourceURL, line;
if (symbolicatedSource == null) {
const {sourceURL, line} = source;
return (
<div
className={styles.SourceOneLiner}
data-testname="InspectedElementView-FormattedSourceString">
{formatSourceForDisplay(sourceURL, line)}
</div>
);
sourceURL = source.sourceURL;
line = source.line;
} else {
sourceURL = symbolicatedSource.sourceURL;
line = symbolicatedSource.line;
}
const {sourceURL, line} = symbolicatedSource;
return (
<div
className={styles.SourceOneLiner}
data-testname="InspectedElementView-FormattedSourceString">
{formatSourceForDisplay(sourceURL, line)}
{linkIsEnabled ? (
<span className={styles.Link} onClick={viewSource}>
{formatSourceForDisplay(sourceURL, line)}
</span>
) : (
formatSourceForDisplay(sourceURL, line)
)}
</div>
);
}