Files
moreminimore-vibe/src/hooks/useRunApp.ts
2025-05-08 17:21:35 -07:00

163 lines
4.6 KiB
TypeScript

import { useState, useCallback } from "react";
import { IpcClient } from "@/ipc/ipc_client";
import {
appOutputAtom,
appUrlAtom,
currentAppAtom,
previewPanelKeyAtom,
previewErrorMessageAtom,
selectedAppIdAtom,
} from "@/atoms/appAtoms";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
export function useRunApp() {
const [loading, setLoading] = useState(false);
const [app, setApp] = useAtom(currentAppAtom);
const setAppOutput = useSetAtom(appOutputAtom);
const [appUrlObj, setAppUrlObj] = useAtom(appUrlAtom);
const setPreviewPanelKey = useSetAtom(previewPanelKeyAtom);
const appId = useAtomValue(selectedAppIdAtom);
const setPreviewErrorMessage = useSetAtom(previewErrorMessageAtom);
const runApp = useCallback(async (appId: number) => {
setLoading(true);
try {
const ipcClient = IpcClient.getInstance();
console.debug("Running app", appId);
// Clear the URL and add restart message
if (appUrlObj?.appId !== appId) {
setAppUrlObj({ appUrl: null, appId: null });
}
setAppOutput((prev) => [
...prev,
{
message: "Trying to restart app...",
type: "stdout",
appId,
timestamp: Date.now(),
},
]);
const app = await ipcClient.getApp(appId);
setApp(app);
await ipcClient.runApp(appId, (output) => {
setAppOutput((prev) => [...prev, output]);
// Check if the output contains a localhost URL
const urlMatch = output.message.match(/(https?:\/\/localhost:\d+\/?)/);
if (urlMatch) {
setAppUrlObj({ appUrl: urlMatch[1], appId });
}
});
setPreviewErrorMessage(undefined);
} catch (error) {
console.error(`Error running app ${appId}:`, error);
setPreviewErrorMessage(
error instanceof Error ? error.message : error?.toString(),
);
} finally {
setLoading(false);
}
}, []);
const stopApp = useCallback(async (appId: number) => {
if (appId === null) {
return;
}
setLoading(true);
try {
const ipcClient = IpcClient.getInstance();
await ipcClient.stopApp(appId);
setPreviewErrorMessage(undefined);
} catch (error) {
console.error(`Error stopping app ${appId}:`, error);
setPreviewErrorMessage(
error instanceof Error ? error.message : error?.toString(),
);
} finally {
setLoading(false);
}
}, []);
const onHotModuleReload = useCallback(() => {
setPreviewPanelKey((prevKey) => prevKey + 1);
}, [setPreviewPanelKey]);
const restartApp = useCallback(
async ({
removeNodeModules = false,
}: { removeNodeModules?: boolean } = {}) => {
if (appId === null) {
return;
}
setLoading(true);
try {
const ipcClient = IpcClient.getInstance();
console.debug(
"Restarting app",
appId,
removeNodeModules ? "with node_modules cleanup" : "",
);
// Clear the URL and add restart message
setAppUrlObj({ appUrl: null, appId: null });
setAppOutput((prev) => [
...prev,
{
message: "Restarting app...",
type: "stdout",
appId,
timestamp: Date.now(),
},
]);
const app = await ipcClient.getApp(appId);
setApp(app);
await ipcClient.restartApp(
appId,
(output) => {
setAppOutput((prev) => [...prev, output]);
if (
output.message.includes("hmr update") &&
output.message.includes("[vite]")
) {
onHotModuleReload();
return;
}
// Check if the output contains a localhost URL
const urlMatch = output.message.match(
/(https?:\/\/localhost:\d+\/?)/,
);
if (urlMatch) {
setAppUrlObj({ appUrl: urlMatch[1], appId });
}
},
removeNodeModules,
);
} catch (error) {
console.error(`Error restarting app ${appId}:`, error);
setPreviewErrorMessage(
error instanceof Error ? error.message : error?.toString(),
);
} finally {
setPreviewPanelKey((prevKey) => prevKey + 1);
setLoading(false);
}
},
[appId, setApp, setAppOutput, setAppUrlObj, setPreviewPanelKey],
);
const refreshAppIframe = useCallback(async () => {
setPreviewPanelKey((prevKey) => prevKey + 1);
}, [setPreviewPanelKey]);
return {
loading,
runApp,
stopApp,
restartApp,
app,
refreshAppIframe,
};
}