Skip to content

Commit a3f45d2

Browse files
drfarrellclaude
andcommitted
refactor: address CodeRabbit review comments for restart sandbox feature
- Extract restart logic into reusable handleRestartSandbox function (DRY principle) - Add comprehensive error handling with try-catch blocks - Add error logging for debugging - Ensure consistent toast notifications in both views - Add frame-specific error handling when refreshing webviews - Add custom RestartSandbox icon with cube and refresh symbol 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 804a1ea commit a3f45d2

File tree

2 files changed

+66
-27
lines changed

2 files changed

+66
-27
lines changed

apps/web/client/src/app/project/[id]/_components/bottom-bar/terminal-area.tsx

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,42 @@ export const TerminalArea = observer(({ children }: { children: React.ReactNode
4949

5050
const [terminalHidden, setTerminalHidden] = useState(true);
5151

52+
// Extract restart logic into a reusable function to follow DRY principles
53+
const handleRestartSandbox = async () => {
54+
const activeBranch = branches.activeBranch;
55+
if (!activeBranch) return;
56+
57+
try {
58+
const sandbox = branches.getSandboxById(activeBranch.id);
59+
if (!sandbox?.session) {
60+
toast.error('Sandbox session not available');
61+
return;
62+
}
63+
64+
const success = await sandbox.session.restartDevServer();
65+
if (success) {
66+
// Refresh all webviews for the active branch
67+
const frames = editorEngine.frames.getAll();
68+
frames.forEach(frame => {
69+
try {
70+
editorEngine.frames.reloadView(frame.frame.id);
71+
} catch (frameError) {
72+
console.error('Failed to reload frame:', frame.frame.id, frameError);
73+
}
74+
});
75+
76+
toast.success('Sandbox restarted successfully', {
77+
icon: <Icons.Cube className="h-4 w-4" />,
78+
});
79+
} else {
80+
toast.error('Failed to restart sandbox');
81+
}
82+
} catch (error) {
83+
console.error('Error restarting sandbox:', error);
84+
toast.error('An error occurred while restarting the sandbox');
85+
}
86+
};
87+
5288
return (
5389
<>
5490
{terminalHidden ? (
@@ -57,22 +93,7 @@ export const TerminalArea = observer(({ children }: { children: React.ReactNode
5793
<Tooltip>
5894
<TooltipTrigger asChild>
5995
<button
60-
onClick={async () => {
61-
const activeBranch = branches.activeBranch;
62-
if (activeBranch) {
63-
const sandbox = branches.getSandboxById(activeBranch.id);
64-
if (sandbox?.session) {
65-
const success = await sandbox.session.restartDevServer();
66-
if (success) {
67-
toast.success('Sandbox restarted successfully', {
68-
icon: <Icons.Cube className="h-4 w-4" />,
69-
});
70-
} else {
71-
toast.error('Failed to restart sandbox');
72-
}
73-
}
74-
}
75-
}}
96+
onClick={handleRestartSandbox}
7697
disabled={!branches.activeBranch}
7798
className={cn(
7899
"h-9 w-9 flex items-center justify-center rounded-md border border-transparent",
@@ -81,7 +102,7 @@ export const TerminalArea = observer(({ children }: { children: React.ReactNode
81102
: "text-foreground-disabled cursor-not-allowed opacity-50"
82103
)}
83104
>
84-
<Icons.Reload className="h-4 w-4" />
105+
<Icons.RestartSandbox className="h-4 w-4" />
85106
</button>
86107
</TooltipTrigger>
87108
<TooltipContent sideOffset={5} hideArrow>Restart Sandbox</TooltipContent>
@@ -117,15 +138,7 @@ export const TerminalArea = observer(({ children }: { children: React.ReactNode
117138
<Tooltip>
118139
<TooltipTrigger asChild>
119140
<button
120-
onClick={async () => {
121-
const activeBranch = branches.activeBranch;
122-
if (activeBranch) {
123-
const sandbox = branches.getSandboxById(activeBranch.id);
124-
if (sandbox?.session) {
125-
await sandbox.session.restartDevServer();
126-
}
127-
}
128-
}}
141+
onClick={handleRestartSandbox}
129142
disabled={!branches.activeBranch}
130143
className={cn(
131144
"h-9 w-9 flex items-center justify-center rounded-md border border-transparent",
@@ -134,7 +147,7 @@ export const TerminalArea = observer(({ children }: { children: React.ReactNode
134147
: "text-foreground-disabled cursor-not-allowed opacity-50"
135148
)}
136149
>
137-
<Icons.Reload className="h-4 w-4" />
150+
<Icons.RestartSandbox className="h-4 w-4" />
138151
</button>
139152
</TooltipTrigger>
140153
<TooltipContent sideOffset={5} hideArrow>Restart Sandbox</TooltipContent>

packages/ui/src/components/icons/index.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,6 +1668,32 @@ export const Icons = {
16681668

16691669
QuestionMarkCircled: QuestionMarkCircledIcon,
16701670
Reload: ReloadIcon,
1671+
RestartSandbox: ({ className, ...props }: IconProps) => (
1672+
<svg
1673+
width="15"
1674+
height="15"
1675+
viewBox="0 0 15 15"
1676+
fill="none"
1677+
xmlns="http://www.w3.org/2000/svg"
1678+
className={className}
1679+
{...props}
1680+
>
1681+
<path
1682+
d="M7.15637 7.49942V12.8119M7.15637 7.49942L2.46875 4.86269M7.15637 7.49942L11.6954 4.94612M8.74562 12.4062L7.76906 12.9555C7.38856 13.1695 6.92394 13.1695 6.54344 12.9555L2.63718 10.7582C2.24358 10.5368 2 10.1204 2 9.66873V5.33008C2 4.87849 2.24358 4.46201 2.63718 4.24061L6.54344 2.04335C6.92394 1.8293 7.38856 1.8293 7.76906 2.04335L11.6753 4.24061C12.0689 4.46201 12.3125 4.87849 12.3125 5.33008V6.41474V6.70047"
1683+
stroke="currentColor"
1684+
strokeWidth="0.84"
1685+
strokeLinecap="round"
1686+
strokeLinejoin="round"
1687+
/>
1688+
<path
1689+
d="M11.498 8L11.5 10M10 8.89185C9.68883 9.24413 9.5 9.70702 9.5 10.214C9.5 11.3175 10.3945 12.212 11.498 12.212C12.6015 12.212 13.496 11.3175 13.496 10.214C13.496 9.70702 13.3072 9.24413 12.996 8.89185"
1690+
stroke="currentColor"
1691+
strokeWidth="0.8"
1692+
strokeLinecap="round"
1693+
strokeLinejoin="round"
1694+
/>
1695+
</svg>
1696+
),
16711697
Reset: ResetIcon,
16721698
RowSpacing: RowSpacingIcon,
16731699

0 commit comments

Comments
 (0)