Skip to content
Closed
Show file tree
Hide file tree
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 @@ -7,13 +7,15 @@ import { observer } from 'mobx-react-lite';
import { motion } from 'motion/react';
import { useState } from 'react';
import { Terminal } from './terminal';
import { toast } from '@onlook/ui/sonner';

export const TerminalArea = observer(({ children }: { children: React.ReactNode }) => {
const editorEngine = useEditorEngine();
const terminalSessions = editorEngine.sandbox.session.terminalSessions;
const activeSessionId = editorEngine.sandbox.session.activeTerminalSessionId;

const [terminalHidden, setTerminalHidden] = useState(true);
const [isRestarting, setIsRestarting] = useState(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

Avoid per-component restart flags; centralize in store

If there are multiple restart entry points (e.g., CodeControls), a local isRestarting can allow concurrent restarts from other surfaces. Prefer a single observable flag on editorEngine.sandbox.session (e.g., isRestartingDevServer) or have restartDevServer() self-gate reentry and expose status.

🤖 Prompt for AI Agents
In apps/web/client/src/app/project/[id]/_components/bottom-bar/terminal-area.tsx
around line 18, you currently use a per-component isRestarting local state which
allows concurrent restarts from other UI surfaces; replace it by relying on a
single centralized restart status (preferably an observable on
editorEngine.sandbox.session like isRestartingDevServer) or make
restartDevServer self-gate reentry and expose its status. Remove the useState
hook, read/observe the centralized flag (or the restartDevServer promise/status)
to render UI and disable controls, and ensure restartDevServer sets/clears that
shared flag (or returns a single in-flight promise) so multiple components
cannot trigger concurrent restarts.


if (!terminalSessions.size) {
return (
Expand All @@ -24,6 +26,23 @@ export const TerminalArea = observer(({ children }: { children: React.ReactNode
)
}

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);
}
}

return (
<>
{terminalHidden ? (
Expand Down Expand Up @@ -57,6 +76,17 @@ export const TerminalArea = observer(({ children }: { children: React.ReactNode
</motion.span>
<div className="flex items-center gap-1">
<motion.div layout>{/* <RunButton /> */}</motion.div>
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={() => handleRestartDevServer()}
className="h-9 w-9 flex items-center justify-center hover:text-foreground-hover text-foreground-tertiary hover:bg-accent/50 rounded-md border border-transparent"
>
<Icons.Reload className={cn("h-4 w-4", isRestarting && "animate-spin")}/>
</button>
</TooltipTrigger>
<TooltipContent sideOffset={5} hideArrow>Restart dev server</TooltipContent>
</Tooltip>
Comment on lines +79 to +89
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Disable during restart + a11y + avoid extra closure

  • Disable the control while restarting; add aria-label/aria-busy for accessibility.
  • Pass the handler reference directly to avoid recreating a closure every render.
  • If your Tooltip is Radix-based, disabled buttons won’t trigger tooltips; wrap the button with a <span> if you need the tooltip even when disabled.
-                            <TooltipTrigger asChild>
-                                <button
-                                    onClick={() => handleRestartDevServer()}
-                                    className="h-9 w-9 flex items-center justify-center hover:text-foreground-hover text-foreground-tertiary hover:bg-accent/50 rounded-md border border-transparent"
-                                >
-                                    <Icons.Reload className={cn("h-4 w-4", isRestarting && "animate-spin")}/>
-                                </button>
-                            </TooltipTrigger>
+                            <TooltipTrigger asChild>
+                                <button
+                                    onClick={handleRestartDevServer}
+                                    disabled={isRestarting}
+                                    aria-label="Restart dev server"
+                                    aria-busy={isRestarting}
+                                    className={cn(
+                                      "h-9 w-9 flex items-center justify-center rounded-md border border-transparent",
+                                      "text-foreground-tertiary hover:text-foreground-hover hover:bg-accent/50",
+                                      "disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none"
+                                    )}
+                                >
+                                    <Icons.Reload
+                                      className={cn("h-4 w-4", isRestarting && "animate-spin")}
+                                      aria-hidden="true"
+                                    />
+                                </button>
+                            </TooltipTrigger>

This also aligns with the PR description claiming the button is disabled during restart.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={() => handleRestartDevServer()}
className="h-9 w-9 flex items-center justify-center hover:text-foreground-hover text-foreground-tertiary hover:bg-accent/50 rounded-md border border-transparent"
>
<Icons.Reload className={cn("h-4 w-4", isRestarting && "animate-spin")}/>
</button>
</TooltipTrigger>
<TooltipContent sideOffset={5} hideArrow>Restart dev server</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={handleRestartDevServer}
disabled={isRestarting}
aria-label="Restart dev server"
aria-busy={isRestarting}
className={cn(
"h-9 w-9 flex items-center justify-center rounded-md border border-transparent",
"text-foreground-tertiary hover:text-foreground-hover hover:bg-accent/50",
"disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none"
)}
>
<Icons.Reload
className={cn("h-4 w-4", isRestarting && "animate-spin")}
aria-hidden="true"
/>
</button>
</TooltipTrigger>
<TooltipContent sideOffset={5} hideArrow>
Restart dev server
</TooltipContent>
</Tooltip>

<Tooltip>
<TooltipTrigger asChild>
<button
Expand Down
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,10 @@ export const CodeControls = observer(() => {
})();
const files = editorEngine.ide.files;


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