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