diff --git a/src/components/InsightsPanel.ts b/src/components/InsightsPanel.ts index 6314b8391..a06c00375 100644 --- a/src/components/InsightsPanel.ts +++ b/src/components/InsightsPanel.ts @@ -409,10 +409,24 @@ export class InsightsPanel extends Panel { this.renderInsights(importantClusters, sentiments, worldBrief); } catch (error) { console.error('[InsightsPanel] Error:', error); - this.setContent('
Analysis failed - retrying...
'); + const reason = InsightsPanel.classifyError(error); + this.setContent(`
Analysis failed (${reason}) - retrying...
`); } } + /** Classify an error into a short user-facing category string. */ + private static classifyError(err: unknown): string { + if (err instanceof DOMException && err.name === 'AbortError') return 'request cancelled'; + if (err instanceof TypeError && /fetch|network/i.test(err.message)) return 'network error'; + if (err instanceof Error) { + const msg = err.message.toLowerCase(); + if (msg.includes('timeout') || msg.includes('timed out')) return 'timeout'; + if (msg.includes('rate') || msg.includes('429') || msg.includes('quota')) return 'rate limited'; + if (msg.includes('5') && msg.includes('00')) return 'server error'; + } + return 'unexpected error'; + } + private renderInsights( clusters: ClusteredEvent[], sentiments: Array<{ label: string; score: number }> | null, diff --git a/src/components/NewsPanel.ts b/src/components/NewsPanel.ts index 09bf9bd90..a236b0fe3 100644 --- a/src/components/NewsPanel.ts +++ b/src/components/NewsPanel.ts @@ -166,11 +166,14 @@ export class NewsPanel extends Panel { this.setCachedSummary(cacheKey, result.summary); this.showSummary(result.summary); } else { - this.summaryContainer.innerHTML = '
Could not generate summary
'; + this.summaryContainer.innerHTML = '
Summary unavailable (no providers responded)
'; + console.warn('[NewsPanel] generateSummary returned null — all providers exhausted'); setTimeout(() => this.hideSummary(), 3000); } - } catch { - this.summaryContainer.innerHTML = '
Summary failed
'; + } catch (err) { + const reason = NewsPanel.classifyError(err); + this.summaryContainer.innerHTML = `
Summary failed (${reason})
`; + console.warn('[NewsPanel] Summary error:', err); setTimeout(() => this.hideSummary(), 3000); } finally { this.isSummarizing = false; @@ -230,6 +233,20 @@ export class NewsPanel extends Panel { this.summaryContainer.innerHTML = ''; } + /** Classify an error into a short user-facing category string. */ + private static classifyError(err: unknown): string { + if (err instanceof DOMException && err.name === 'AbortError') return 'request cancelled'; + if (err instanceof TypeError && /fetch|network/i.test(err.message)) return 'network error'; + if (err instanceof Error) { + const msg = err.message.toLowerCase(); + if (msg.includes('timeout') || msg.includes('timed out')) return 'timeout'; + if (msg.includes('rate') || msg.includes('429') || msg.includes('quota')) return 'rate limited'; + if (msg.includes('401') || msg.includes('403') || msg.includes('unauthorized')) return 'auth error'; + if (msg.includes('5') && msg.includes('00')) return 'server error'; + } + return 'unexpected error'; + } + private getHeadlineSignature(): string { return JSON.stringify(this.currentHeadlines.slice(0, 5).sort()); } diff --git a/src/main.ts b/src/main.ts index 0b64b01ba..bc7cec7a4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -284,8 +284,12 @@ if (!('__TAURI_INTERNALS__' in window) && !('__TAURI__' in window) && 'serviceWo console.log('[PWA] Service worker registered'); setInterval(async () => { if (!navigator.onLine) return; - try { await registration.update(); } catch {} + try { await registration.update(); } catch (updateErr) { + console.warn('[PWA] Service worker update check failed:', updateErr); + } }, 60 * 60 * 1000); }) - .catch(() => {}); + .catch((error) => { + console.warn('[PWA] Service worker registration failed:', error); + }); }