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