diff --git a/package-lock.json b/package-lock.json
index e095c9e42..5c34c0018 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6382,7 +6382,6 @@
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
"integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@epic-web/invariant": "^1.0.0",
"cross-spawn": "^7.0.6"
diff --git a/src/components/Panel.ts b/src/components/Panel.ts
index 56cdfbaf4..11a5980ec 100644
--- a/src/components/Panel.ts
+++ b/src/components/Panel.ts
@@ -3,6 +3,7 @@ import { invokeTauri } from '../services/tauri-bridge';
import { t } from '../services/i18n';
import { h, replaceChildren, safeHtml } from '../utils/dom-utils';
import { trackPanelResized } from '@/services/analytics';
+import { getAiFlowSettings } from '@/services/ai-flow-settings';
export interface PanelOptions {
id: string;
@@ -661,7 +662,13 @@ export class Panel {
public setCount(count: number): void {
if (this.countEl) {
+ const prev = parseInt(this.countEl.textContent ?? '0', 10);
this.countEl.textContent = count.toString();
+ if (count > prev && getAiFlowSettings().badgeAnimation) {
+ this.countEl.classList.remove('bump');
+ void this.countEl.offsetWidth;
+ this.countEl.classList.add('bump');
+ }
}
}
diff --git a/src/components/UnifiedSettings.ts b/src/components/UnifiedSettings.ts
index 8f12d0f52..befd77a69 100644
--- a/src/components/UnifiedSettings.ts
+++ b/src/components/UnifiedSettings.ts
@@ -172,6 +172,8 @@ export class UnifiedSettings {
this.updateAiStatus();
} else if (target.id === 'us-map-flash') {
setAiFlowSetting('mapNewsFlash', target.checked);
+ } else if (target.id === 'us-badge-anim') {
+ setAiFlowSetting('badgeAnimation', target.checked);
}
});
@@ -288,6 +290,10 @@ export class UnifiedSettings {
html += `
${t('components.insights.sectionMap')}
`;
html += this.toggleRowHtml('us-map-flash', t('components.insights.mapFlashLabel'), t('components.insights.mapFlashDesc'), settings.mapNewsFlash);
+ // Panels section
+ html += `${t('components.insights.sectionPanels')}
`;
+ html += this.toggleRowHtml('us-badge-anim', t('components.insights.badgeAnimLabel'), t('components.insights.badgeAnimDesc'), settings.badgeAnimation);
+
// AI Analysis section (web-only)
if (!this.config.isDesktopApp) {
html += `${t('components.insights.sectionAi')}
`;
diff --git a/src/locales/ar.json b/src/locales/ar.json
index 890f37aba..7223c03ab 100644
--- a/src/locales/ar.json
+++ b/src/locales/ar.json
@@ -910,6 +910,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "Cloud AI (Groq & OpenRouter)",
"aiFlowCloudDesc": "إرسال العناوين إلى السحابة لتلخيص الذكاء الاصطناعي (موصى به)",
diff --git a/src/locales/de.json b/src/locales/de.json
index 3f41e4a1f..6b3fc8bcf 100644
--- a/src/locales/de.json
+++ b/src/locales/de.json
@@ -1176,6 +1176,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "Cloud-KI (Groq & OpenRouter)",
"aiFlowCloudDesc": "Schlagzeilen zur KI-Zusammenfassung an die Cloud senden (empfohlen)",
diff --git a/src/locales/el.json b/src/locales/el.json
index ff6ee5785..41d20e856 100644
--- a/src/locales/el.json
+++ b/src/locales/el.json
@@ -949,6 +949,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "Cloud AI (Groq & OpenRouter)",
"aiFlowCloudDesc": "Αποστολή τίτλων στο cloud για σύνοψη AI (συνιστάται)",
diff --git a/src/locales/en.json b/src/locales/en.json
index 711cb18c1..46533f608 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -981,6 +981,9 @@
"streamQualityDesc": "Set quality for all live streams (lower saves bandwidth)",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "Cloud AI (Groq & OpenRouter)",
"aiFlowCloudDesc": "Send headlines to cloud for AI summarization (recommended)",
diff --git a/src/locales/es.json b/src/locales/es.json
index cbc56686e..7c98a8911 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -1176,6 +1176,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "IA en la nube (Groq & OpenRouter)",
"aiFlowCloudDesc": "Enviar titulares a la nube para resumen con IA (recomendado)",
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 8ff2cca5a..3df9d3e28 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -910,6 +910,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "IA Cloud (Groq & OpenRouter)",
"aiFlowCloudDesc": "Envoyer les titres au cloud pour le résumé IA (recommandé)",
diff --git a/src/locales/it.json b/src/locales/it.json
index 71ba27df7..97456c063 100644
--- a/src/locales/it.json
+++ b/src/locales/it.json
@@ -1176,6 +1176,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "IA Cloud (Groq & OpenRouter)",
"aiFlowCloudDesc": "Invia i titoli al cloud per il riepilogo IA (consigliato)",
diff --git a/src/locales/ja.json b/src/locales/ja.json
index d0cd735cd..8929c740e 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -910,6 +910,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "クラウドAI(Groq & OpenRouter)",
"aiFlowCloudDesc": "見出しをクラウドに送信してAI要約(推奨)",
diff --git a/src/locales/ko.json b/src/locales/ko.json
index 997ee882f..a871d8e21 100644
--- a/src/locales/ko.json
+++ b/src/locales/ko.json
@@ -967,6 +967,9 @@
"streamQualityDesc": "모든 라이브 스트림의 품질 설정 (낮추면 대역폭 절약)",
"mapFlashLabel": "실시간 이벤트 펄스",
"mapFlashDesc": "속보 수신 시 지도에서 해당 위치를 깜박임",
+ "sectionPanels": "패널",
+ "badgeAnimLabel": "카운트 배지 펄스",
+ "badgeAnimDesc": "새 항목이 도착하면 패널 카운트 배지에 애니메이션 적용",
"aiFlowTitle": "설정",
"aiFlowCloudLabel": "클라우드 AI (Groq 및 OpenRouter)",
"aiFlowCloudDesc": "헤드라인을 클라우드로 전송하여 AI 요약 (권장)",
diff --git a/src/locales/nl.json b/src/locales/nl.json
index c2abbf3e7..fbc75fe97 100644
--- a/src/locales/nl.json
+++ b/src/locales/nl.json
@@ -979,6 +979,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "Cloud AI (Groq & OpenRouter)",
"aiFlowCloudDesc": "Koppen naar de cloud sturen voor AI-samenvatting (aanbevolen)",
diff --git a/src/locales/pl.json b/src/locales/pl.json
index b93bde453..5a5b61e30 100644
--- a/src/locales/pl.json
+++ b/src/locales/pl.json
@@ -1176,6 +1176,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "Cloud AI (Groq & OpenRouter)",
"aiFlowCloudDesc": "Wyślij nagłówki do chmury w celu podsumowania AI (zalecane)",
diff --git a/src/locales/pt.json b/src/locales/pt.json
index f328d5872..682b7c194 100644
--- a/src/locales/pt.json
+++ b/src/locales/pt.json
@@ -979,6 +979,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "IA na nuvem (Groq & OpenRouter)",
"aiFlowCloudDesc": "Enviar manchetes para a nuvem para resumo IA (recomendado)",
diff --git a/src/locales/ru.json b/src/locales/ru.json
index cb4c0138e..f35a31049 100644
--- a/src/locales/ru.json
+++ b/src/locales/ru.json
@@ -910,6 +910,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "Облачный ИИ (Groq & OpenRouter)",
"aiFlowCloudDesc": "Отправлять заголовки в облако для ИИ-суммирования (рекомендуется)",
diff --git a/src/locales/sv.json b/src/locales/sv.json
index c7a13b2e0..26d797d37 100644
--- a/src/locales/sv.json
+++ b/src/locales/sv.json
@@ -979,6 +979,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "Moln-AI (Groq & OpenRouter)",
"aiFlowCloudDesc": "Skicka rubriker till molnet för AI-sammanfattning (rekommenderat)",
diff --git a/src/locales/th.json b/src/locales/th.json
index ba0d1a2d0..7ba588364 100644
--- a/src/locales/th.json
+++ b/src/locales/th.json
@@ -910,6 +910,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "Cloud AI (Groq & OpenRouter)",
"aiFlowCloudDesc": "ส่งพาดหัวข่าวไปยังคลาวด์เพื่อสรุปด้วย AI (แนะนำ)",
diff --git a/src/locales/tr.json b/src/locales/tr.json
index ddfe0a11b..d28bbc303 100644
--- a/src/locales/tr.json
+++ b/src/locales/tr.json
@@ -910,6 +910,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "Bulut AI (Groq & OpenRouter)",
"aiFlowCloudDesc": "Basliklari AI ozetleme icin buluta gonder (onerilen)",
diff --git a/src/locales/vi.json b/src/locales/vi.json
index 9b2c36f07..9152d68cb 100644
--- a/src/locales/vi.json
+++ b/src/locales/vi.json
@@ -910,6 +910,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "Cloud AI (Groq & OpenRouter)",
"aiFlowCloudDesc": "Gửi tiêu đề tới cloud để AI tóm tắt (khuyến nghị)",
diff --git a/src/locales/zh.json b/src/locales/zh.json
index 5bcd31e1a..a44932569 100644
--- a/src/locales/zh.json
+++ b/src/locales/zh.json
@@ -910,6 +910,9 @@
"sectionAi": "AI Analysis",
"mapFlashLabel": "Live Event Pulse",
"mapFlashDesc": "Flash locations on the map when breaking news arrives",
+ "sectionPanels": "Panels",
+ "badgeAnimLabel": "Count Badge Pulse",
+ "badgeAnimDesc": "Animate panel count badges when new items arrive",
"aiFlowTitle": "Settings",
"aiFlowCloudLabel": "云端AI(Groq & OpenRouter)",
"aiFlowCloudDesc": "将标题发送到云端进行AI摘要(推荐)",
diff --git a/src/services/ai-flow-settings.ts b/src/services/ai-flow-settings.ts
index 6ebe04916..5bda907f8 100644
--- a/src/services/ai-flow-settings.ts
+++ b/src/services/ai-flow-settings.ts
@@ -9,6 +9,7 @@
const STORAGE_KEY_BROWSER_MODEL = 'wm-ai-flow-browser-model';
const STORAGE_KEY_CLOUD_LLM = 'wm-ai-flow-cloud-llm';
const STORAGE_KEY_MAP_NEWS_FLASH = 'wm-map-news-flash';
+const STORAGE_KEY_BADGE_ANIMATION = 'wm-badge-animation';
const STORAGE_KEY_STREAM_QUALITY = 'wm-stream-quality';
const EVENT_NAME = 'ai-flow-changed';
const STREAM_QUALITY_EVENT = 'stream-quality-changed';
@@ -17,6 +18,7 @@ export interface AiFlowSettings {
browserModel: boolean;
cloudLlm: boolean;
mapNewsFlash: boolean;
+ badgeAnimation: boolean;
}
function readBool(key: string, defaultValue: boolean): boolean {
@@ -41,12 +43,14 @@ const STORAGE_KEY_MAP: Record = {
browserModel: STORAGE_KEY_BROWSER_MODEL,
cloudLlm: STORAGE_KEY_CLOUD_LLM,
mapNewsFlash: STORAGE_KEY_MAP_NEWS_FLASH,
+ badgeAnimation: STORAGE_KEY_BADGE_ANIMATION,
};
const DEFAULTS: AiFlowSettings = {
browserModel: false,
cloudLlm: true,
mapNewsFlash: true,
+ badgeAnimation: false,
};
export function getAiFlowSettings(): AiFlowSettings {
@@ -54,6 +58,7 @@ export function getAiFlowSettings(): AiFlowSettings {
browserModel: readBool(STORAGE_KEY_BROWSER_MODEL, DEFAULTS.browserModel),
cloudLlm: readBool(STORAGE_KEY_CLOUD_LLM, DEFAULTS.cloudLlm),
mapNewsFlash: readBool(STORAGE_KEY_MAP_NEWS_FLASH, DEFAULTS.mapNewsFlash),
+ badgeAnimation: readBool(STORAGE_KEY_BADGE_ANIMATION, DEFAULTS.badgeAnimation),
};
}
diff --git a/src/styles/main.css b/src/styles/main.css
index 966cb3153..220b22edb 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -1061,6 +1061,17 @@ body.panel-resize-active iframe {
background: var(--border);
padding: 2px 6px;
border-radius: 2px;
+ transition: color 0.3s ease, background 0.3s ease;
+}
+
+.panel-count.bump {
+ animation: count-bump 0.5s ease-out;
+}
+
+@keyframes count-bump {
+ 0% { transform: scale(1); background: var(--border); color: var(--text-dim); }
+ 40% { transform: scale(1.3); background: var(--accent); color: var(--bg); }
+ 100% { transform: scale(1); background: var(--border); color: var(--text-dim); }
}