@samitouri / QOS-React / commits / bb4418d647

[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" />

Sebastian Markbåge committed Jul 21, 2025 at 17:36 UTC bb4418d6470b95c7d487f3b73a9dc980edff6f06
2 files changed +50 -12
packages/react-devtools-shared/src/devtools/views/Components/InspectedElementSourcePanel.css
+15
@@ -18,3 +18,18 @@
18 max-width: 100%;
19 margin-left: 1rem;
20 }
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
+35 -12
@@ -8,6 +8,7 @@
8 */
9
10 import * as React from 'react';
11 +import {useCallback, useContext} from 'react';
12 import {copy} from 'clipboard-js';
13 import {toNormalUrl} from 'jsc-safe-url';
14
@@ -16,6 +17,8 @@ import ButtonIcon from '../ButtonIcon';
17 import Skeleton from './Skeleton';
18 import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck';
19
20 +import ViewElementSourceContext from './ViewElementSourceContext';
21 +
22 import type {Source as InspectedElementSource} from 'react-devtools-shared/src/shared/types';
23 import styles from './InspectedElementSourcePanel.css';
24
@@ -87,25 +90,45 @@ function CopySourceButton({source, symbolicatedSourcePromise}: Props) {
90
91 function FormattedSourceString({source, symbolicatedSourcePromise}: Props) {
92 const symbolicatedSource = React.use(symbolicatedSourcePromise);
90 - if (symbolicatedSource == null) {
91 - const {sourceURL, line} = source;
93
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]);
111
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 + }
120
121 return (
122 <div
123 className={styles.SourceOneLiner}
124 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 + )}
132 </div>
133 );
134 }