Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { TooltipArrow } from '@radix-ui/react-tooltip';
import { cn } from '@onlook/ui/utils';
import { observer } from 'mobx-react-lite';
import { useState } from 'react';
import { toast } from '@onlook/ui/sonner';
import { FileModal } from './file-modal';
import { FolderModal } from './folder-modal';
import { UploadModal } from './upload-modal';
Expand All @@ -21,6 +22,7 @@ export const CodeControls = observer(() => {
const [fileModalOpen, setFileModalOpen] = useState(false);
const [folderModalOpen, setFolderModalOpen] = useState(false);
const [uploadModalOpen, setUploadModalOpen] = useState(false);
const [isRestarting, setIsRestarting] = useState(false);
const isDirty = editorEngine.ide.activeFile?.isDirty ?? false;

const saveFile = () => {
Expand All @@ -36,9 +38,26 @@ export const CodeControls = observer(() => {
})();
const files = editorEngine.ide.files;

const handleRestartDevServer = async () => {
try {
if (isRestarting) return;
setIsRestarting(true);
const restartResponse = await editorEngine.sandbox.session.restartDevServer();
if (restartResponse) {
toast.success('Dev server restarting');
} else {
toast.error('Failed to restart dev server');
}
} catch (err) {
toast.error('Error restarting dev server');
} finally {
setIsRestarting(false);
}
}
Comment on lines +41 to +56
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Harden restart handler: atomic reentrancy guard, null-check engine path, better error detail

  • Use the ref guard to prevent concurrent calls.
  • Null-check editorEngine?.sandbox?.session?.restartDevServer to avoid runtime errors when the session is unavailable.
  • Log error details for debugging and surface a concise user-facing toast.
-const handleRestartDevServer = async () => {
-    try {
-        if (isRestarting) return;
-        setIsRestarting(true);
-        const restartResponse = await editorEngine.sandbox.session.restartDevServer();
-        if (restartResponse) {
-            toast.success('Dev server restarting');
-        } else {
-            toast.error('Failed to restart dev server');
-        }
-    } catch (err) {
-        toast.error('Error restarting dev server');
-    } finally {
-        setIsRestarting(false);
-    }
-}
+const handleRestartDevServer = async () => {
+    if (restartingRef.current) return; // fast re-entry guard
+    restartingRef.current = true;
+    setIsRestarting(true);
+    try {
+        const restartFn = editorEngine?.sandbox?.session?.restartDevServer;
+        if (typeof restartFn !== 'function') {
+            toast.error('Restart unavailable in this context');
+            return;
+        }
+        const ok = await restartFn();
+        if (ok) {
+            toast.success('Dev server restart requested');
+        } else {
+            toast.error('Failed to request dev server restart');
+        }
+    } catch (err) {
+        // Keep the toast concise, but log details for diagnostics
+        // eslint-disable-next-line no-console
+        console.error('Error restarting dev server', err);
+        const msg = err instanceof Error ? err.message : 'Unknown error';
+        toast.error(`Error restarting dev server: ${msg}`);
+    } finally {
+        setIsRestarting(false);
+        restartingRef.current = false;
+    }
+};

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/code-controls.tsx
around lines 41 to 56, the restart handler lacks an atomic reentrancy guard,
doesn't null-check the nested engine path before invoking restartDevServer, and
swallows error details; add a useRef boolean (e.g., restartingRef) to serve as
the atomic guard (check and set it immediately at function start and clear in
finally), verify editorEngine?.sandbox?.session?.restartDevServer is a function
before awaiting it (return early with a user toast if not available), and catch
errors by logging detailed error info (console.error or app logger) while
showing a concise toast message to the user; ensure the ref and setIsRestarting
are cleared in finally so state is consistent.


return (
<>
<div className="flex flex-row opacity-50 transition-opacity duration-200 group-hover/panel:opacity-100">
<div className="flex flex-row items-center gap-1 opacity-50 transition-opacity duration-200 group-hover/panel:opacity-100">
<Tooltip>
<DropdownMenu>
<TooltipTrigger asChild>
Expand Down Expand Up @@ -74,6 +93,24 @@ export const CodeControls = observer(() => {
<TooltipArrow className="fill-foreground" />
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
disabled={isRestarting}
onClick={() => handleRestartDevServer()}
className="p-2 w-fit h-fit hover:bg-background-onlook cursor-pointer"
aria-label="Restart dev server"
>
<Icons.Reload className={cn("h-4 w-4", isRestarting && "animate-spin")}/>
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" hideArrow>
<p>Restart dev server</p>
<TooltipArrow className="fill-foreground" />
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
Expand Down