Unverified 提交 d9b6b548 authored 作者: Nour Zakhma's avatar Nour Zakhma 提交者: GitHub

feat: Add React DevTools in development (#3112)

probleme : Before this change, developers couldn't inspect React components in the Electron app's DevTools. The DevTools only showed the Elements tab (DOM), not the ️ React Components tab. This made debugging React components much harder. solution: We added automatic loading of React DevTools extension when running in development mode, so the React Components tab is available in Electron DevTools. Two simple files changed: [main.ts]→ Added React DevTools extension loading code [package.json] → Added npm run dev script New script: "dev": "cross-env NODE_ENV=development npm start" This sets [NODE_ENV=development]( so React DevTools loads 📝 Steps to Enable This Feature For Developers Using Dyad Step 1: Install React DevTools Extension in Chrome Open Google Chrome Go to: [https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi] Click "Add to Chrome" Confirm the installation Step 2: Run the App in Dev Mode expected behavior <img width="1591" height="870" alt="Capture d&#39;écran 2026-04-01 152857" src="https://github.com/user-attachments/assets/341130e8-38ac-4d61-84cb-5415b248278d" /> <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/3112" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end -->
上级 1e56c111
......@@ -17,6 +17,7 @@
"bump": "node scripts/bump-version.mjs",
"clean": "rimraf out scaffold/node_modules",
"start": "electron-forge start",
"dev": "cross-env NODE_ENV=development npm start",
"dev:engine": "cross-env DYAD_ENGINE_URL=http://localhost:8080/v1 npm start",
"staging:engine": "cross-env DYAD_ENGINE_URL=https://staging---dyad-llm-engine-kq7pivehnq-uc.a.run.app/v1 npm start",
"package": "npm run clean && electron-forge package",
......
import { app, BrowserWindow, dialog, Menu, protocol, net } from "electron";
import {
app,
BrowserWindow,
dialog,
Menu,
protocol,
net,
session,
} from "electron";
import * as path from "node:path";
import { registerIpcHandlers } from "./ipc/ipc_host";
import dotenv from "dotenv";
......@@ -87,6 +95,73 @@ if (process.defaultApp) {
}
export async function onReady() {
// Load React DevTools extension in development
if (process.env.NODE_ENV === "development") {
let chromeUserData: string;
// Determine Chrome extensions path based on platform
if (process.platform === "win32") {
chromeUserData = path.join(
process.env.LOCALAPPDATA || "",
"Google",
"Chrome",
"User Data",
"Default",
"Extensions",
);
} else if (process.platform === "darwin") {
// macOS
chromeUserData = path.join(
process.env.HOME || "",
"Library",
"Application Support",
"Google",
"Chrome",
"Default",
"Extensions",
);
} else {
// Linux
chromeUserData = path.join(
process.env.HOME || "",
".config",
"google-chrome",
"Default",
"Extensions",
);
}
// React DevTools extension ID
const reactDevToolsId = "fmkadmapgofadopljbjfkapdkoienihi";
const extensionsDir = path.join(chromeUserData, reactDevToolsId);
if (fs.existsSync(extensionsDir)) {
try {
const versions = fs.readdirSync(extensionsDir);
if (versions.length > 0) {
// Get the latest version using numeric sort to handle version boundaries (e.g., 9.0.0 vs 10.0.0)
const latestVersion = versions
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
.reverse()[0];
const extensionPath = path.join(extensionsDir, latestVersion);
await session.defaultSession.loadExtension(extensionPath, {
allowFileAccess: true,
});
logger.info("React DevTools loaded successfully");
} else {
logger.warn(
"React DevTools extension directory is empty. Install it in Chrome first.",
);
}
} catch (err) {
logger.error("Failed to load React DevTools:", err);
}
} else {
logger.warn(
"React DevTools extension not found. Install it in Chrome first.",
);
}
}
try {
const backupManager = new BackupManager({
settingsFile: getSettingsFilePath(),
......@@ -275,6 +350,19 @@ const createWindow = () => {
// backgroundColor: "#00000001",
// frame: false,
});
// In development, wait for DevTools to open, then reload the page once so React DevTools initializes correctly
if (process.env.NODE_ENV === "development") {
mainWindow.webContents.once("devtools-opened", () => {
setTimeout(() => {
const windowRef = mainWindow;
if (!windowRef?.isDestroyed()) {
windowRef?.webContents.reloadIgnoringCache();
}
}, 300);
});
mainWindow.webContents.openDevTools();
}
// and load the index.html of the app.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
......@@ -283,20 +371,32 @@ const createWindow = () => {
path.join(__dirname, "../renderer/main_window/index.html"),
);
}
// Handle force-close message and development reload coordination
let forceCloseMessageSent = false;
let devToolsReloadedCount = 0;
mainWindow.webContents.on("did-finish-load", () => {
if (process.env.NODE_ENV === "development") {
// Open the DevTools.
mainWindow.webContents.openDevTools();
// In dev, wait until AFTER the DevTools-triggered reload before sending the message
if (devToolsReloadedCount === 0) {
devToolsReloadedCount++;
return; // Ignore first load, we will reload momentarily
}
}
// Send force-close event if it was detected
if (pendingForceCloseData) {
mainWindow.webContents.once("did-finish-load", () => {
mainWindow?.webContents.send("force-close-detected", {
// Send force-close once after the correct load
if (pendingForceCloseData && !forceCloseMessageSent) {
forceCloseMessageSent = true;
const windowRef = mainWindow;
if (!windowRef?.isDestroyed()) {
windowRef?.webContents.send("force-close-detected", {
performanceData: pendingForceCloseData,
});
}
pendingForceCloseData = null;
});
}
});
// Enable native context menu on right-click
mainWindow.webContents.on("context-menu", (event, params) => {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论