feat: restore GLM 4.7 fixes - auto-scroll and retry logic
Changes from GLM 4.7 Progress Log: 1. Multi-task chat auto-scroll (multi-task-chat.tsx): - Added createEffect that monitors message count changes - Auto-scrolls using requestAnimationFrame + setTimeout(50ms) - Scrolls when new messages arrive or during streaming 2. Electron black screen fix (main.ts): - Added exponential backoff retry (1s, 2s, 4s, 8s, 16s max) - Added 30-second timeout for load operations - Added user-friendly error screen with retry button - Handles errno -3 network errors gracefully - Max 5 retry attempts before showing error
This commit is contained in:
@@ -18,6 +18,11 @@ let pendingCliUrl: string | null = null
|
||||
let showingLoadingScreen = false
|
||||
let preloadingView: BrowserView | null = null
|
||||
|
||||
// Retry logic constants
|
||||
const MAX_RETRY_ATTEMPTS = 5
|
||||
const LOAD_TIMEOUT_MS = 30000
|
||||
let retryAttempts = 0
|
||||
|
||||
if (isMac) {
|
||||
app.commandLine.appendSwitch("disable-spell-checking")
|
||||
}
|
||||
@@ -89,6 +94,61 @@ function loadLoadingScreen(window: BrowserWindow) {
|
||||
})
|
||||
}
|
||||
|
||||
// Calculate exponential backoff delay
|
||||
function getRetryDelay(attempt: number): number {
|
||||
return Math.min(1000 * Math.pow(2, attempt), 16000) // 1s, 2s, 4s, 8s, 16s max
|
||||
}
|
||||
|
||||
// Show user-friendly error screen
|
||||
function showErrorScreen(window: BrowserWindow, errorMessage: string) {
|
||||
const errorHtml = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 40px;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
background: #1a1a1a;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.error-icon { font-size: 48px; margin-bottom: 20px; }
|
||||
h1 { margin: 0 0 16px; font-size: 24px; font-weight: 600; }
|
||||
p { margin: 0 0 24px; color: #888; font-size: 14px; text-align: center; max-width: 400px; }
|
||||
.error-code { font-family: monospace; background: #2a2a2a; padding: 8px 16px; border-radius: 6px; font-size: 12px; color: #f87171; margin-bottom: 24px; }
|
||||
button {
|
||||
background: #6366f1;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 32px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
button:hover { background: #818cf8; transform: scale(1.02); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error-icon">⚠️</div>
|
||||
<h1>Connection Failed</h1>
|
||||
<p>NomadArch couldn't connect to the development server after multiple attempts. Please ensure the server is running.</p>
|
||||
<div class="error-code">${errorMessage}</div>
|
||||
<button onclick="location.reload()">Retry</button>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
window.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(errorHtml)}`)
|
||||
}
|
||||
|
||||
function getAllowedRendererOrigins(): string[] {
|
||||
const origins = new Set<string>()
|
||||
const rendererCandidates = [currentCliUrl, process.env.VITE_DEV_SERVER_URL, process.env.ELECTRON_RENDERER_URL]
|
||||
@@ -305,7 +365,55 @@ function finalizeCliSwap(url: string) {
|
||||
showingLoadingScreen = false
|
||||
currentCliUrl = url
|
||||
pendingCliUrl = null
|
||||
mainWindow.loadURL(url).catch((error) => console.error("[cli] failed to load CLI view:", error))
|
||||
|
||||
// Reset retry counter on new URL
|
||||
retryAttempts = 0
|
||||
|
||||
const loadWithRetry = () => {
|
||||
if (!mainWindow || mainWindow.isDestroyed()) return
|
||||
|
||||
// Set timeout for load
|
||||
const timeoutId = setTimeout(() => {
|
||||
console.warn(`[cli] Load timeout after ${LOAD_TIMEOUT_MS}ms`)
|
||||
handleLoadError(new Error(`Load timeout after ${LOAD_TIMEOUT_MS}ms`))
|
||||
}, LOAD_TIMEOUT_MS)
|
||||
|
||||
mainWindow.loadURL(url)
|
||||
.then(() => {
|
||||
clearTimeout(timeoutId)
|
||||
retryAttempts = 0 // Reset on success
|
||||
console.info("[cli] Successfully loaded CLI view")
|
||||
})
|
||||
.catch((error) => {
|
||||
clearTimeout(timeoutId)
|
||||
handleLoadError(error)
|
||||
})
|
||||
}
|
||||
|
||||
const handleLoadError = (error: Error) => {
|
||||
const errorCode = (error as any).errno
|
||||
console.error(`[cli] failed to load CLI view (attempt ${retryAttempts + 1}/${MAX_RETRY_ATTEMPTS}):`, error.message)
|
||||
|
||||
// Retry on network errors (errno -3)
|
||||
if (errorCode === -3 && retryAttempts < MAX_RETRY_ATTEMPTS) {
|
||||
retryAttempts++
|
||||
const delay = getRetryDelay(retryAttempts)
|
||||
console.info(`[cli] Retrying in ${delay}ms (attempt ${retryAttempts}/${MAX_RETRY_ATTEMPTS})`)
|
||||
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
loadLoadingScreen(mainWindow)
|
||||
}
|
||||
|
||||
setTimeout(loadWithRetry, delay)
|
||||
} else if (retryAttempts >= MAX_RETRY_ATTEMPTS) {
|
||||
console.error("[cli] Max retry attempts reached, showing error screen")
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
showErrorScreen(mainWindow, `Failed after ${MAX_RETRY_ATTEMPTS} attempts: ${error.message}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadWithRetry()
|
||||
}
|
||||
|
||||
|
||||
@@ -370,7 +478,7 @@ app.whenReady().then(() => {
|
||||
|
||||
app.on("before-quit", async (event) => {
|
||||
event.preventDefault()
|
||||
await cliManager.stop().catch(() => {})
|
||||
await cliManager.stop().catch(() => { })
|
||||
app.exit(0)
|
||||
})
|
||||
|
||||
|
||||
@@ -149,6 +149,19 @@ export default function MultiTaskChat(props: MultiTaskChatProps) {
|
||||
return () => clearInterval(interval);
|
||||
});
|
||||
|
||||
// Auto-scroll when new messages arrive
|
||||
createEffect(() => {
|
||||
const ids = filteredMessageIds();
|
||||
const thinking = isAgentThinking();
|
||||
|
||||
// Scroll when message count changes or when thinking starts
|
||||
if (ids.length > 0 || thinking) {
|
||||
requestAnimationFrame(() => {
|
||||
setTimeout(scrollToBottom, 50);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const handleSendMessage = async () => {
|
||||
const message = chatInput().trim();
|
||||
if (!message || isSending()) return;
|
||||
|
||||
Reference in New Issue
Block a user