Skip to content

Conversation

Surajsuthar
Copy link
Contributor

@Surajsuthar Surajsuthar commented Aug 25, 2025

Description

Added button to restart dev server

Related Issues

"bug #2749"

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Release
  • Refactor
  • Other (please describe):

Testing

Screenshots (if applicable)

Screen.Recording.2025-08-25.at.2.38.38.PM.mov

Additional Notes


Important

Adds a "Restart dev server" button to CodeControls with loading state and user feedback using toast.

  • Behavior:
    • Adds a "Restart dev server" button in CodeControls component in code-controls.tsx.
    • Button is disabled during restart process, indicated by isRestarting state.
    • Displays success or error message using toast based on restart outcome.
  • UI:
    • Button includes a tooltip with label "Restart dev server".
    • Button icon spins during restart to indicate loading.
  • State Management:
    • Introduces isRestarting state to manage button's disabled state and loading animation.

This description was created by Ellipsis for f7606a6. You can customize this summary. It will automatically update as commits are pushed.

Summary by CodeRabbit

  • New Features

    • Added a Restart Dev Server button in the Dev tab for one-click restarts.
    • Provides real-time feedback via success/error notifications.
    • Shows a loading spinner and temporarily disables the button during restart to prevent duplicate actions.
  • UX/UI

    • Improved control bar layout with updated alignment and spacing to accommodate the new button.

Copy link

vercel bot commented Aug 25, 2025

@Surajsuthar is attempting to deploy a commit to the Onlook Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

coderabbitai bot commented Aug 25, 2025

Walkthrough

Adds a dev-server restart flow to CodeControls: introduces an isRestarting state, an async handleRestartDevServer calling editorEngine.sandbox.session.restartDevServer(), toast feedback for outcomes, a Restart Dev Server button with disabled/spinner states, and layout adjustments.

Changes

Cohort / File(s) Summary
Dev tab controls UI & behavior
apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/code-controls.tsx
Added isRestarting state; implemented handleRestartDevServer async flow calling editorEngine.sandbox.session.restartDevServer(); integrated toast notifications for success/failure/errors; added a Restart Dev Server button (ghost, icon-size) with disabled and spinner states; adjusted container classes for alignment/spacing.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CodeControls
  participant SandboxSession as editorEngine.sandbox.session
  participant Toast

  User->>CodeControls: Click "Restart Dev Server"
  CodeControls->>CodeControls: set isRestarting = true (disable button, show spinner)
  CodeControls->>SandboxSession: restartDevServer()
  alt success
    SandboxSession-->>CodeControls: resolves
    CodeControls->>Toast: toast.success("Dev server restarted")
  else failure/error
    SandboxSession-->>CodeControls: rejects / throws
    CodeControls->>Toast: toast.error("Failed to restart dev server")
  end
  CodeControls->>CodeControls: finally set isRestarting = false (enable button, hide spinner)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

I press a tiny button, ears aflutter,
The server blinks and shakes off clutter.
Toasts chime bright, the spin subsides,
Code restarts swift on rolling tides.
A rabbit smiles—dev joy delivered. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/code-controls.tsx (3)

96-112: Prevent floating promise; keep tooltip usable when disabled

  • Add void before calling the async handler in onClick to satisfy no-floating-promises rules and keep consistency with saveFile usage.
  • Optional: disabled buttons often don’t trigger tooltips. Wrapping the button with a span as the TooltipTrigger keeps the tooltip accessible when disabled (same improvement applies to other disabled icon buttons in this file as a follow-up).

Apply this diff:

-                    <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>
+                    <TooltipTrigger asChild>
+                        <span>
+                            <Button
+                                variant="ghost"
+                                size="icon"
+                                disabled={isRestarting}
+                                onClick={() => void 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>
+                        </span>
+                    </TooltipTrigger>

15-15: Import toast from the UI wrapper for consistent theming

The UI package’s sonner.tsx wrapper (packages/ui/src/components/sonner.tsx) re-exports toast from “sonner”, ensuring it flows through our design tokens/providers. Importing directly from "sonner" bypasses our theming. Please update this file accordingly:

• File: apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/code-controls.tsx
Line 15:

-import { toast } from 'sonner';
+import { toast } from '@onlook/ui/sonner';

You may also want to run a repo-wide check for other direct "sonner" imports and replace them with @onlook/ui/sonner for uniform styling.


41-55: Harden restart handler: rename, debounce, enrich errors

A few optional tweaks to make this handler more robust and self-documenting. None are strictly required given the current API, but they’ll improve readability and fault-tolerance:

• Rename the response variable for consistency

- const reStartResponse = await editorEngine.sandbox.session.restartDevServer();
+ const restartResponse   = await editorEngine.sandbox.session.restartDevServer();

—matches the Promise<boolean> return type and conventional camelCase.

• Prevent concurrent restarts

if (isRestarting) return;
setIsRestarting(true);

—early-return stops double-clicks or repeated calls from queuing up.

• Simplify the boolean check

if (restartResponse) {
  toast.success('Dev server restart initiated');
} else {
  toast.error('Failed to restart dev server');
}

Since restartDevServer(): Promise<boolean> always resolves to true or false, you can drop the undefined-handling branch.

• Log and surface detailed errors

} catch (err) {
  console.error('restartDevServer failed', err);
  const message = err instanceof Error ? err.message : String(err);
  toast.error(`Error restarting dev server: ${message}`);
} finally {
  setIsRestarting(false);
}

—captures stack traces in the console and shows the underlying message in the toast.

Overall, these are optional refactors to tighten up naming, prevent duplicate executions, and improve observability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ebb953a and f7606a6.

📒 Files selected for processing (1)
  • apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/code-controls.tsx (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/code-controls.tsx (5)
packages/ui/src/components/sonner.tsx (1)
  • toast (19-19)
packages/ui/src/components/tooltip.tsx (3)
  • Tooltip (72-72)
  • TooltipTrigger (72-72)
  • TooltipContent (72-72)
packages/ui/src/components/button.tsx (1)
  • Button (57-57)
packages/ui/src/components/icons/index.tsx (1)
  • Icons (137-3592)
apps/web/template/lib/utils.ts (1)
  • cn (4-6)
🔇 Additional comments (2)
apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/code-controls.tsx (2)

25-25: State flag looks good

Using a simple boolean for in-flight state is appropriate here and aligns with the rest of the component.


59-59: UI container tweaks LGTM

The alignment/gap/opacity transitions read well and preserve the existing hover affordance.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/code-controls.tsx (3)

25-25: Guard against ultra-fast double clicks with a ref, not just state

State updates are async; two rapid clicks before the re-render can slip past the isRestarting check. Add a ref as a synchronous, in-memory guard while keeping state for UI.

-import { useState } from 'react';
+import { useRef, useState } from 'react';
@@
-const [isRestarting, setIsRestarting] = useState(false);
+const [isRestarting, setIsRestarting] = useState(false);
+const restartingRef = useRef(false);

96-113: Minor: pass handler directly; expose busy state; cursor when disabled

  • Pass the handler directly to avoid creating a new function per render.
  • Advertise busy state to assistive tech (aria-busy).
  • Optionally swap the cursor when disabled for clearer affordance.
-<Button
+<Button
     variant="ghost"
     size="icon"
     disabled={isRestarting}
-    onClick={() => handleRestartDevServer()}
-    className="p-2 w-fit h-fit hover:bg-background-onlook cursor-pointer"
+    onClick={handleRestartDevServer}
+    aria-busy={isRestarting}
+    className={cn(
+      "p-2 w-fit h-fit hover:bg-background-onlook",
+      isRestarting ? "cursor-not-allowed" : "cursor-pointer"
+    )}
     aria-label="Restart dev server"
 >
     <Icons.Reload className={cn("h-4 w-4", isRestarting && "animate-spin")}/>
 </Button>

41-56: Add a brief test plan (and optional unit test) for the restart flow

Given this is user-triggered infra control, please add a lightweight test plan to the PR description and consider a unit test that mocks the engine and verifies UI feedback.

Suggested manual test steps:

  • Open a project and trigger “Restart dev server”.
  • Expect: button disables, icon spins, toast says “Restart requested”.
  • While spinning, attempt multiple clicks: no additional calls should be made.
  • Simulate a failure (make restartDevServer reject): expect error toast and re-enabled button.

Optional Jest/RTL sketch:

// CodeControls.restart.test.tsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { CodeControls } from './code-controls';

jest.mock('@/components/store/editor', () => ({
  useEditorEngine: () => ({
    sandbox: { session: { restartDevServer: jest.fn().mockResolvedValue(true) } },
    ide: { activeFile: { isDirty: false }, files: [], saveActiveFile: jest.fn() },
  }),
}));

test('invokes restart once and disables while in-flight', async () => {
  render(<CodeControls />);
  const btn = await screen.findByRole('button', { name: /restart dev server/i });
  fireEvent.click(btn);
  // second click should be ignored
  fireEvent.click(btn);
  await waitFor(() => expect(btn).toHaveAttribute('aria-busy', 'true'));
  // assert toast and re-enable happen
  await waitFor(() => expect(btn).toHaveAttribute('aria-busy', 'false'));
});

If you’d like, I can open a follow-up PR to add this test.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f7606a6 and 0fd880f.

📒 Files selected for processing (1)
  • apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/code-controls.tsx (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/code-controls.tsx (2)
packages/ui/src/components/sonner.tsx (1)
  • toast (19-19)
packages/ui/src/components/icons/index.tsx (1)
  • Icons (137-3592)
🔇 Additional comments (2)
apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/code-controls.tsx (2)

15-15: LGTM: toast import

Import looks correct and consistent with the UI package usage.


60-60: LGTM: container spacing and hover behavior

The added alignment and gap styles integrate well with adjacent controls.

Comment on lines +41 to +56
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);
}
}
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.

@Kitenite
Copy link
Contributor

Oh this is nice. Could we move the button to next to the Dev Server CLI on the bottom bar perhaps? @Surajsuthar
@drfarrell lmk if you have design feedback on where that should go

@Surajsuthar
Copy link
Contributor Author

@Kitenite sure if you have any specific design lmk

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants