Skip to content

Commit 1f0ab5e

Browse files
committed
fix: add loading screen and retry logic to prevent webview lockup on startup
- Created LoadingScreen component to show proper loading state instead of blank screen - Added timeout handling to show message when loading takes longer than expected - Implemented retry logic in App and ExtensionStateContext to resend initialization message - Changed App to render LoadingScreen instead of null when waiting for state hydration This fixes the issue where the webview would show a blank screen indefinitely if the extension was slow to respond with the initial state. Fixes #6234
1 parent 0504199 commit 1f0ab5e

File tree

3 files changed

+60
-2
lines changed

3 files changed

+60
-2
lines changed

webview-ui/src/App.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { AccountView } from "./components/account/AccountView"
2525
import { useAddNonInteractiveClickListener } from "./components/ui/hooks/useNonInteractiveClick"
2626
import { TooltipProvider } from "./components/ui/tooltip"
2727
import { STANDARD_TOOLTIP_DELAY } from "./components/ui/standard-tooltip"
28+
import { LoadingScreen } from "./components/common/LoadingScreen"
2829

2930
type Tab = "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "account"
3031

@@ -81,6 +82,7 @@ const App = () => {
8182

8283
const [showAnnouncement, setShowAnnouncement] = useState(false)
8384
const [tab, setTab] = useState<Tab>("chat")
85+
const [showTimeout, setShowTimeout] = useState(false)
8486

8587
const [humanRelayDialogState, setHumanRelayDialogState] = useState<HumanRelayDialogState>({
8688
isOpen: false,
@@ -207,6 +209,27 @@ const App = () => {
207209
console.debug("App initialized with source map support")
208210
}, [])
209211

212+
// Add timeout handling for extension state
213+
useEffect(() => {
214+
if (!didHydrateState) {
215+
// Show timeout message after 5 seconds
216+
const timeoutTimer = setTimeout(() => {
217+
setShowTimeout(true)
218+
}, 5000)
219+
220+
// Retry sending webviewDidLaunch after 3 seconds
221+
const retryTimer = setTimeout(() => {
222+
console.debug("Retrying webviewDidLaunch message...")
223+
vscode.postMessage({ type: "webviewDidLaunch" })
224+
}, 3000)
225+
226+
return () => {
227+
clearTimeout(timeoutTimer)
228+
clearTimeout(retryTimer)
229+
}
230+
}
231+
}, [didHydrateState])
232+
210233
// Focus the WebView when non-interactive content is clicked (only in editor/tab mode)
211234
useAddNonInteractiveClickListener(
212235
useCallback(() => {
@@ -224,7 +247,7 @@ const App = () => {
224247
}, [tab])
225248

226249
if (!didHydrateState) {
227-
return null
250+
return <LoadingScreen showTimeout={showTimeout} />
228251
}
229252

230253
// Do not conditionally load ChatView, it's expensive and there's state we
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from "react"
2+
import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
3+
4+
interface LoadingScreenProps {
5+
message?: string
6+
showTimeout?: boolean
7+
}
8+
9+
export const LoadingScreen: React.FC<LoadingScreenProps> = ({
10+
message = "Loading Roo Code...",
11+
showTimeout = false,
12+
}) => {
13+
return (
14+
<div className="fixed inset-0 flex flex-col items-center justify-center bg-vscode-editor-background">
15+
<div className="flex flex-col items-center gap-4">
16+
<VSCodeProgressRing />
17+
<p className="text-vscode-foreground text-sm">{message}</p>
18+
{showTimeout && (
19+
<p className="text-vscode-descriptionForeground text-xs mt-2">Taking longer than expected...</p>
20+
)}
21+
</div>
22+
</div>
23+
)
24+
}

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,8 +360,19 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
360360
}, [handleMessage])
361361

362362
useEffect(() => {
363+
// Send initial launch message
363364
vscode.postMessage({ type: "webviewDidLaunch" })
364-
}, [])
365+
366+
// If we don't get a response within 2 seconds, try again
367+
const retryTimer = setTimeout(() => {
368+
if (!didHydrateState) {
369+
console.warn("No state received from extension, retrying webviewDidLaunch...")
370+
vscode.postMessage({ type: "webviewDidLaunch" })
371+
}
372+
}, 2000)
373+
374+
return () => clearTimeout(retryTimer)
375+
}, [didHydrateState])
365376

366377
const contextValue: ExtensionStateContextType = {
367378
...state,

0 commit comments

Comments
 (0)