Skip to content

Commit bb4418d

Browse files
authored
[DevTools] Linkify Source View (facebook#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" />
1 parent 074e927 commit bb4418d

File tree

2 files changed

+50
-12
lines changed

2 files changed

+50
-12
lines changed

packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,18 @@
1818
max-width: 100%;
1919
margin-left: 1rem;
2020
}
21+
22+
.Link {
23+
color: var(--color-link);
24+
white-space: pre;
25+
overflow: hidden;
26+
text-overflow: ellipsis;
27+
flex: 1;
28+
cursor: pointer;
29+
border-radius: 0.125rem;
30+
padding: 0px 2px;
31+
}
32+
33+
.Link:hover {
34+
background-color: var(--color-background-hover);
35+
}

packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.js

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import * as React from 'react';
11+
import {useCallback, useContext} from 'react';
1112
import {copy} from 'clipboard-js';
1213
import {toNormalUrl} from 'jsc-safe-url';
1314

@@ -16,6 +17,8 @@ import ButtonIcon from '../ButtonIcon';
1617
import Skeleton from './Skeleton';
1718
import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck';
1819

20+
import ViewElementSourceContext from './ViewElementSourceContext';
21+
1922
import type {Source as InspectedElementSource} from 'react-devtools-shared/src/shared/types';
2023
import styles from './InspectedElementSourcePanel.css';
2124

@@ -87,25 +90,45 @@ function CopySourceButton({source, symbolicatedSourcePromise}: Props) {
8790

8891
function FormattedSourceString({source, symbolicatedSourcePromise}: Props) {
8992
const symbolicatedSource = React.use(symbolicatedSourcePromise);
90-
if (symbolicatedSource == null) {
91-
const {sourceURL, line} = source;
9293

93-
return (
94-
<div
95-
className={styles.SourceOneLiner}
96-
data-testname="InspectedElementView-FormattedSourceString">
97-
{formatSourceForDisplay(sourceURL, line)}
98-
</div>
99-
);
100-
}
94+
const {canViewElementSourceFunction, viewElementSourceFunction} = useContext(
95+
ViewElementSourceContext,
96+
);
97+
98+
// In some cases (e.g. FB internal usage) the standalone shell might not be able to view the source.
99+
// To detect this case, we defer to an injected helper function (if present).
100+
const linkIsEnabled =
101+
viewElementSourceFunction != null &&
102+
source != null &&
103+
(canViewElementSourceFunction == null ||
104+
canViewElementSourceFunction(source, symbolicatedSource));
105+
106+
const viewSource = useCallback(() => {
107+
if (viewElementSourceFunction != null && source != null) {
108+
viewElementSourceFunction(source, symbolicatedSource);
109+
}
110+
}, [source, symbolicatedSource]);
101111

102-
const {sourceURL, line} = symbolicatedSource;
112+
let sourceURL, line;
113+
if (symbolicatedSource == null) {
114+
sourceURL = source.sourceURL;
115+
line = source.line;
116+
} else {
117+
sourceURL = symbolicatedSource.sourceURL;
118+
line = symbolicatedSource.line;
119+
}
103120

104121
return (
105122
<div
106123
className={styles.SourceOneLiner}
107124
data-testname="InspectedElementView-FormattedSourceString">
108-
{formatSourceForDisplay(sourceURL, line)}
125+
{linkIsEnabled ? (
126+
<span className={styles.Link} onClick={viewSource}>
127+
{formatSourceForDisplay(sourceURL, line)}
128+
</span>
129+
) : (
130+
formatSourceForDisplay(sourceURL, line)
131+
)}
109132
</div>
110133
);
111134
}

0 commit comments

Comments
 (0)