Reload env path instead of restarting dyad app after node.js install

This commit is contained in:
Will Chen
2025-04-18 10:11:59 -07:00
parent 8d9aab214c
commit cc0ab0fd56
4 changed files with 62 additions and 24 deletions

View File

@@ -21,6 +21,12 @@ import {
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { NodeSystemInfo } from "@/ipc/ipc_types"; import { NodeSystemInfo } from "@/ipc/ipc_types";
type NodeInstallStep =
| "install"
| "waiting-for-continue"
| "continue-processing";
export function SetupBanner() { export function SetupBanner() {
const navigate = useNavigate(); const navigate = useNavigate();
const { isAnyProviderSetup, loading } = useSettings(); const { isAnyProviderSetup, loading } = useSettings();
@@ -28,7 +34,8 @@ export function SetupBanner() {
null null
); );
const [nodeCheckError, setNodeCheckError] = useState<boolean>(false); const [nodeCheckError, setNodeCheckError] = useState<boolean>(false);
const [nodeInstallLoading, setNodeInstallLoading] = useState<boolean>(false); const [nodeInstallStep, setNodeInstallStep] =
useState<NodeInstallStep>("install");
const checkNode = useCallback(async () => { const checkNode = useCallback(async () => {
try { try {
setNodeCheckError(false); setNodeCheckError(false);
@@ -52,14 +59,16 @@ export function SetupBanner() {
}); });
}; };
const handleNodeInstallClick = async () => { const handleNodeInstallClick = useCallback(async () => {
setNodeInstallLoading(true); setNodeInstallStep("waiting-for-continue");
IpcClient.getInstance().openExternalUrl(nodeSystemInfo!.nodeDownloadUrl); IpcClient.getInstance().openExternalUrl(nodeSystemInfo!.nodeDownloadUrl);
}; }, [nodeSystemInfo, setNodeInstallStep]);
const finishNodeInstallAndRestart = () => { const finishNodeInstall = useCallback(async () => {
IpcClient.getInstance().reloadDyad(); setNodeInstallStep("continue-processing");
}; await IpcClient.getInstance().reloadEnvPath();
await checkNode();
}, [checkNode, setNodeInstallStep]);
const isNodeSetupComplete = Boolean( const isNodeSetupComplete = Boolean(
nodeSystemInfo?.nodeVersion && nodeSystemInfo?.pnpmVersion nodeSystemInfo?.nodeVersion && nodeSystemInfo?.pnpmVersion
@@ -123,7 +132,7 @@ export function SetupBanner() {
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{getStatusIcon(isNodeSetupComplete, nodeCheckError)} {getStatusIcon(isNodeSetupComplete, nodeCheckError)}
<span className="font-medium text-sm"> <span className="font-medium text-sm">
1. Install Node.js 1. Install Node.js (App Runtime)
</span> </span>
</div> </div>
</div> </div>
@@ -146,17 +155,37 @@ export function SetupBanner() {
) : ( ) : (
<div className="text-sm"> <div className="text-sm">
<p>Node.js is required to run apps locally.</p> <p>Node.js is required to run apps locally.</p>
<p className="mb-3"> {nodeInstallStep === "waiting-for-continue" && (
After you have installed node.js, click "Finish setup" to <p className="mt-1">
restart Dyad. After you have installed Node.js, click "Continue". If the
</p> installer didn't work, try{" "}
{nodeInstallLoading ? ( <a
<Button onClick={finishNodeInstallAndRestart}> className="text-blue-500 dark:text-blue-400 hover:underline"
Finish setup and restart Dyad onClick={() => {
IpcClient.getInstance().openExternalUrl(
"https://nodejs.org/en/download"
);
}}
>
more download options
</a>
.
</p>
)}
{nodeInstallStep === "install" ? (
<Button className="mt-3" onClick={handleNodeInstallClick}>
Install Node.js Runtime
</Button> </Button>
) : ( ) : (
<Button onClick={handleNodeInstallClick}> <Button className="mt-3" onClick={finishNodeInstall}>
Install Node.js Runtime {nodeInstallStep === "continue-processing" ? (
<div className="flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
Checking Node.js setup...
</div>
) : (
"Continue | I installed Node.js"
)}
</Button> </Button>
)} )}
</div> </div>

View File

@@ -1,7 +1,8 @@
import { ipcMain, app } from "electron"; import { ipcMain, app } from "electron";
import { spawn } from "child_process"; import { exec, execSync, spawn } from "child_process";
import { platform, arch } from "os"; import { platform, arch } from "os";
import { NodeSystemInfo } from "../ipc_types"; import { NodeSystemInfo } from "../ipc_types";
import fixPath from "fix-path";
function checkCommandExists(command: string): Promise<string | null> { function checkCommandExists(command: string): Promise<string | null> {
return new Promise((resolve) => { return new Promise((resolve) => {
@@ -68,8 +69,16 @@ export function registerNodeHandlers() {
return { nodeVersion, pnpmVersion, nodeDownloadUrl }; return { nodeVersion, pnpmVersion, nodeDownloadUrl };
}); });
ipcMain.handle("reload-dyad", async (): Promise<void> => { ipcMain.handle("reload-env-path", async (): Promise<void> => {
app.relaunch(); console.debug("Reloading env path, previously:", process.env.PATH);
app.exit(0); if (platform() === "win32") {
const newPath = execSync("cmd /c echo %PATH%", {
encoding: "utf8",
}).trim();
process.env.PATH = newPath;
} else {
fixPath();
}
console.debug("Reloaded env path, now:", process.env.PATH);
}); });
} }

View File

@@ -131,8 +131,8 @@ export class IpcClient {
return IpcClient.instance; return IpcClient.instance;
} }
public async reloadDyad(): Promise<void> { public async reloadEnvPath(): Promise<void> {
await this.ipcRenderer.invoke("reload-dyad"); await this.ipcRenderer.invoke("reload-env-path");
} }
// Create a new app with an initial chat // Create a new app with an initial chat

View File

@@ -37,7 +37,7 @@ const validInvokeChannels = [
"github:create-repo", "github:create-repo",
"github:push", "github:push",
"get-app-version", "get-app-version",
"reload-dyad", "reload-env-path",
] as const; ] as const;
// Add valid receive channels // Add valid receive channels