1
/* global chrome */
2
3
+import type {SourceSelection} from 'react-devtools-shared/src/devtools/views/Editor/EditorPane';
4
+
5
import {createElement} from 'react';
6
import {flushSync} from 'react-dom';
7
import {createRoot} from 'react-dom/client';
75
);
76
});
77
78
+ const sourcesPanel = chrome.devtools.panels.sources;
79
+
80
const onBrowserElementSelectionChanged = () =>
81
setReactSelectionFromBrowser(bridge);
82
+ const onBrowserSourceSelectionChanged = (location: {
83
+ url: string,
84
+ startLine: number,
85
+ startColumn: number,
86
+ endLine: number,
87
+ endColumn: number,
88
+ }) => {
89
+ if (
90
+ currentSelectedSource === null ||
91
+ currentSelectedSource.url !== location.url
92
+ ) {
93
+ currentSelectedSource = {
94
+ url: location.url,
95
+ selectionRef: {
96
+ // We use 1-based line and column, Chrome provides them 0-based.
97
+ line: location.startLine + 1,
98
+ column: location.startColumn + 1,
99
+ },
100
+ };
101
+ // Rerender with the new file selection.
102
+ render();
103
+ } else {
104
+ // Update the ref to the latest position without updating the url. No need to rerender.
105
+ const selectionRef = currentSelectedSource.selectionRef;
106
+ selectionRef.line = location.startLine + 1;
107
+ selectionRef.column = location.startColumn + 1;
108
+ }
109
+ };
110
const onBridgeShutdown = () => {
111
chrome.devtools.panels.elements.onSelectionChanged.removeListener(
112
onBrowserElementSelectionChanged,
113
);
114
+ if (sourcesPanel) {
115
+ currentSelectedSource = null;
116
+ sourcesPanel.onSelectionChanged.removeListener(
117
+ onBrowserSourceSelectionChanged,
118
+ );
119
+ }
120
};
121
122
bridge.addListener('shutdown', onBridgeShutdown);
124
chrome.devtools.panels.elements.onSelectionChanged.addListener(
125
onBrowserElementSelectionChanged,
126
);
127
+ if (sourcesPanel) {
128
+ sourcesPanel.onSelectionChanged.addListener(
129
+ onBrowserSourceSelectionChanged,
130
+ );
131
+ }
132
}
133
134
function createBridgeAndStore() {
195
bridge,
196
browserTheme: getBrowserTheme(),
197
componentsPortalContainer,
198
+ profilerPortalContainer,
199
+ editorPortalContainer,
200
+ currentSelectedSource,
201
enabledInspectedElementContextMenu: true,
202
fetchFileWithCaching,
203
hookNamesModuleLoaderFunction,
204
overrideTab,
159
- profilerPortalContainer,
205
showTabBar: false,
206
store,
207
warnIfUnsupportedVersionDetected: true,
302
);
303
}
304
305
+function createSourcesEditorPanel() {
306
+ if (editorPortalContainer) {
307
+ // Panel is created and user opened it at least once
308
+ ensureInitialHTMLIsCleared(editorPortalContainer);
309
+ render();
310
+
311
+ return;
312
+ }
313
+
314
+ if (editorPane) {
315
+ // Panel is created, but wasn't opened yet, so no document is present for it
316
+ return;
317
+ }
318
+
319
+ const sourcesPanel = chrome.devtools.panels.sources;
320
+ if (!sourcesPanel) {
321
+ // Firefox doesn't currently support extending the source panel.
322
+ return;
323
+ }
324
+
325
+ sourcesPanel.createSidebarPane('Code Editor ⚛', createdPane => {
326
+ editorPane = createdPane;
327
+
328
+ createdPane.setPage('panel.html');
329
+ createdPane.setHeight('42px');
330
+
331
+ createdPane.onShown.addListener(portal => {
332
+ editorPortalContainer = portal.container;
333
+ if (editorPortalContainer != null && render) {
334
+ ensureInitialHTMLIsCleared(editorPortalContainer);
335
+
336
+ render();
337
+ portal.injectStyles(cloneStyleTags);
338
+
339
+ logEvent({event_name: 'selected-editor-pane'});
340
+ }
341
+ });
342
+
343
+ createdPane.onShown.addListener(() => {
344
+ bridge.emit('extensionEditorPaneShown');
345
+ });
346
+ createdPane.onHidden.addListener(() => {
347
+ bridge.emit('extensionEditorPaneHidden');
348
+ });
349
+ });
350
+}
351
+
352
function performInTabNavigationCleanup() {
353
// Potentially, if react hasn't loaded yet and user performs in-tab navigation
354
clearReactPollingInstance();
448
449
createComponentsPanel();
450
createProfilerPanel();
451
+ createSourcesEditorPanel();
452
}
453
454
let reactPollingInstance = null;
487
488
let componentsPanel = null;
489
let profilerPanel = null;
490
+let editorPane = null;
491
let componentsPortalContainer = null;
492
let profilerPortalContainer = null;
493
+let editorPortalContainer = null;
494
495
let mostRecentOverrideTab = null;
496
let render = null;
497
let root = null;
498
499
+let currentSelectedSource: null | SourceSelection = null;
500
+
501
let port = null;
502
503
// In case when multiple navigation events emitted in a short period of time