Selaa lähdekoodia

feat: HTML 报告浏览器增强(暗色模式/浮动工具栏/一键复制/宽屏模式/Tab导航),修复 AI 分析日志区分,升级至 v6.6.0

sansan 1 kuukausi sitten
vanhempi
sitoutus
b1d09d08ea
10 muutettua tiedostoa jossa 939 lisäystä ja 48 poistoa
  1. 34 14
      README-EN.md
  2. 34 13
      README.md
  3. 1 1
      pyproject.toml
  4. 1 1
      trendradar/__init__.py
  5. 2 0
      trendradar/__main__.py
  6. 3 1
      trendradar/ai/analyzer.py
  7. 20 1
      trendradar/ai/formatter.py
  8. 842 15
      trendradar/report/html.py
  9. 1 1
      uv.lock
  10. 1 1
      version

+ 34 - 14
README-EN.md

@@ -11,7 +11,7 @@ Deploy in <strong>30 seconds</strong> — Say goodbye to endless scrolling, only
 [![GitHub Stars](https://img.shields.io/github/stars/sansan0/TrendRadar?style=flat-square&logo=github&color=yellow)](https://github.com/sansan0/TrendRadar/stargazers)
 [![GitHub Forks](https://img.shields.io/github/forks/sansan0/TrendRadar?style=flat-square&logo=github&color=blue)](https://github.com/sansan0/TrendRadar/network/members)
 [![License](https://img.shields.io/badge/license-GPL--3.0-blue.svg?style=flat-square)](LICENSE)
-[![Version](https://img.shields.io/badge/version-v6.5.5-blue.svg)](https://github.com/sansan0/TrendRadar)
+[![Version](https://img.shields.io/badge/version-v6.6.0-blue.svg)](https://github.com/sansan0/TrendRadar)
 [![MCP](https://img.shields.io/badge/MCP-v4.0.2-green.svg)](https://github.com/sansan0/TrendRadar)
 [![RSS](https://img.shields.io/badge/RSS-Feed_Support-orange.svg?style=flat-square&logo=rss&logoColor=white)](https://github.com/sansan0/TrendRadar)
 [![AI Translation](https://img.shields.io/badge/AI-Multi--Language-purple.svg?style=flat-square)](https://github.com/sansan0/TrendRadar)
@@ -193,20 +193,14 @@ This contributes to the sustainable maintenance of the project and the growth of
 - **Tip**: Check [Changelog] to understand specific [Features]
 
 
-### 2026/03/12 - v6.5.0
-
-- **AI Smart News Filtering**: No more manual keyword setup! Describe your interests in everyday language in `ai_interests.txt` (e.g., "I want AI and renewable energy news"), and AI automatically extracts tags, scores every headline, and only pushes what truly matters to you. If AI filtering encounters issues, it auto-falls back to keyword matching — push delivery never stops
-- **Per-Period Filter Strategy & Interests**: Each time period in Timeline can now independently choose its filtering method and what topics to focus on. For example: mornings use a "tech keyword list" for quick filtering, evenings switch to "finance AI interests" for in-depth AI filtering — same system, different content at different times
-- **AI Analysis Independent from Push Mode**: AI analysis scope can differ from push content. For example: push only delivers new items (avoiding repeated notifications), while AI analyzes the full day's news (capturing complete trends). Each time period can also set its own AI analysis mode
-- **AI Filter Token Savings**: Previously analyzed news won't be re-processed; when you edit your interests, AI auto-evaluates the change magnitude — minor tweaks only update affected tags, major changes trigger full reclassification
-- **Multi-File Config & Tag Isolation**: Custom keyword files go in `config/custom/keyword/`, AI interest files go in `config/custom/ai/` — tags from different files are fully isolated and independent
-- **AI Translation Precision Control**: Independently toggle translation for hotlist, RSS, and standalone sections; regions with display turned off are automatically skipped, saving tokens
-- **Remote Storage Batch Upload**: Multiple write operations are batched and submitted to cloud in one go, reducing API call count
-- **Per-Group Display Limit**: New `max_news_per_keyword` controls max items shown per keyword/tag group, preventing a single hot topic from filling the entire push
-- **Time Period Conflict Detection**: Overlapping time periods are automatically detected — system alerts you to fix the config, preventing unexpected behavior
-- Various bug fixes
-
+### 2026/03/28 - v6.6.0
 
+- **HTML Report Browser Enhancement**: Open the HTML report in a browser to unlock widescreen layout, Tab navigation for keyword groups and standalone sections, real-time title search, and more — email clients still show the original narrow layout with zero regression
+- **Dark Mode**: One-click toggle for dark theme with automatic preference persistence, ideal for nighttime reading
+- **One-Click Copy**: Hover over a news number to copy the title and link instantly for quick sharing
+- **Export Optimization**: Full-page and segmented screenshots merged into a dropdown export button; screenshots auto-revert to clean layout
+- **Keyboard Shortcuts**: `W` widescreen toggle, `D` dark mode, `/` search, `?` view all shortcuts
+- **Reading Progress Bar**: Real-time reading progress displayed at the top of the page
 
 ### 2026/02/09 - mcp-v4.0.0
 
@@ -220,6 +214,19 @@ This contributes to the sustainable maintenance of the project and the growth of
 <details>
 <summary>👉 Click to expand: <strong>Historical Updates</strong></summary>
 
+### 2026/03/12 - v6.5.0
+
+- **AI Smart News Filtering**: No more manual keyword setup! Describe your interests in everyday language in `ai_interests.txt` (e.g., "I want AI and renewable energy news"), and AI automatically extracts tags, scores every headline, and only pushes what truly matters to you. If AI filtering encounters issues, it auto-falls back to keyword matching — push delivery never stops
+- **Per-Period Filter Strategy & Interests**: Each time period in Timeline can now independently choose its filtering method and what topics to focus on. For example: mornings use a "tech keyword list" for quick filtering, evenings switch to "finance AI interests" for in-depth AI filtering — same system, different content at different times
+- **AI Analysis Independent from Push Mode**: AI analysis scope can differ from push content. For example: push only delivers new items (avoiding repeated notifications), while AI analyzes the full day's news (capturing complete trends). Each time period can also set its own AI analysis mode
+- **AI Filter Token Savings**: Previously analyzed news won't be re-processed; when you edit your interests, AI auto-evaluates the change magnitude — minor tweaks only update affected tags, major changes trigger full reclassification
+- **Multi-File Config & Tag Isolation**: Custom keyword files go in `config/custom/keyword/`, AI interest files go in `config/custom/ai/` — tags from different files are fully isolated and independent
+- **AI Translation Precision Control**: Independently toggle translation for hotlist, RSS, and standalone sections; regions with display turned off are automatically skipped, saving tokens
+- **Remote Storage Batch Upload**: Multiple write operations are batched and submitted to cloud in one go, reducing API call count
+- **Per-Group Display Limit**: New `max_news_per_keyword` controls max items shown per keyword/tag group, preventing a single hot topic from filling the entire push
+- **Time Period Conflict Detection**: Overlapping time periods are automatically detected — system alerts you to fix the config, preventing unexpected behavior
+- Various bug fixes
+
 ### 2026/02/09 - v6.0.0
 
 > **Breaking Change**: Config file upgrade (config.yaml 2.0.0), old `push_window` and `analysis_window` configs are no longer compatible, please refer to the new config.yaml for migration
@@ -1041,6 +1048,19 @@ ai_translation:
 
 > ⚠️ Some international media content may involve sensitive topics that AI models might refuse to translate. Please filter subscription sources based on your actual needs
 
+### **HTML Report Browser Enhancement** (v6.6.0 New)
+
+Open the pushed HTML report in a browser to unlock an enhanced experience (email clients are unaffected):
+
+- **Widescreen Mode**: Auto-switches to 1200px wide layout on desktop, making full use of screen space
+- **Tab Navigation**: Both keyword groups and standalone sections support Tab switching — no more endless scrolling
+- **Dark Mode**: One-click dark theme toggle with automatic preference persistence
+- **Live Search**: Press `/` to open the search box and instantly filter news titles
+- **One-Click Copy**: Hover over a news number to copy the title and link
+- **Keyboard Shortcuts**: `W` widescreen, `D` dark mode, `/` search, `?` view all shortcuts
+
+> 💡 All enhancements are built on progressive enhancement — email clients still show the original 600px layout with zero regression
+
 ### **Flexible Storage Architecture (v4.0.0 Major Update)**
 
 **Multi-Backend Support**:

+ 34 - 13
README.md

@@ -12,7 +12,7 @@
 [![GitHub Stars](https://img.shields.io/github/stars/sansan0/TrendRadar?style=flat-square&logo=github&color=yellow)](https://github.com/sansan0/TrendRadar/stargazers)
 [![GitHub Forks](https://img.shields.io/github/forks/sansan0/TrendRadar?style=flat-square&logo=github&color=blue)](https://github.com/sansan0/TrendRadar/network/members)
 [![License](https://img.shields.io/badge/license-GPL--3.0-blue.svg?style=flat-square)](LICENSE)
-[![Version](https://img.shields.io/badge/version-v6.5.5-blue.svg)](https://github.com/sansan0/TrendRadar)
+[![Version](https://img.shields.io/badge/version-v6.6.0-blue.svg)](https://github.com/sansan0/TrendRadar)
 [![MCP](https://img.shields.io/badge/MCP-v4.0.2-green.svg)](https://github.com/sansan0/TrendRadar)
 [![RSS](https://img.shields.io/badge/RSS-订阅源支持-orange.svg?style=flat-square&logo=rss&logoColor=white)](https://github.com/sansan0/TrendRadar)
 [![AI翻译](https://img.shields.io/badge/AI-多语言推送-purple.svg?style=flat-square)](https://github.com/sansan0/TrendRadar)
@@ -241,19 +241,14 @@
 - **提示**:建议查看【历史更新】,明确具体的【功能内容】
 
 
-### 2026/03/12 - v6.5.0
-
-- **AI 智能筛选系统**:不用再手动设关键词!在 `ai_interests.txt` 里用日常语言写下你关注的方向(如"我想看 AI 和新能源相关新闻"),AI 会自动提取标签并对每条新闻打分,只推送真正和你相关的内容。万一 AI 筛选出了问题,会自动切回关键词匹配,推送不中断
-- **每个时段支持不同的筛选方式和关注方向**:Timeline 中的每个时间段现在可以独立设置用什么方式筛选、看什么类型的新闻。比如:早上用"科技关键词"快速过滤,晚上换成"金融 AI 兴趣描述"做深度筛选——同一个系统,不同时段看不同内容
-- **AI 分析范围独立于推送**:AI 分析的数据范围可以和推送内容不同。比如推送只发新增消息(避免重复打扰),但 AI 分析当天全部新闻(看完整趋势)。每个时段也能单独设置 AI 分析模式
-- **AI 筛选智能省钱**:已分析过的新闻不会重复消耗 token;兴趣描述修改后,AI 自动判断变化幅度——小改动只更新受影响的标签,大改动才全量重新分类
-- **多文件配置与标签隔离**:自定义关键词文件放 `config/custom/keyword/`,AI 兴趣文件放 `config/custom/ai/`,不同文件产生的标签各自独立、互不干扰
-- **AI 翻译精准控制**:可分别控制热榜、RSS、独立展示区是否翻译,没开启显示的区域自动跳过,不浪费 token
-- **远程存储批量上传**:多次写操作攒在一起统一提交云端,减少 API 调用次数
-- **每组关键词/标签展示数量限制**:通过 `max_news_per_keyword` 控制每个分组最多显示多少条新闻,避免单个热门话题占满整条推送
-- **时段冲突智能检测**:两个时间段如果有时间重叠,系统会自动报错提醒修改,避免配置冲突导致意外行为
-- 修复若干bug
+### 2026/03/28 - v6.6.0
 
+- **HTML 报告浏览器增强**:在浏览器中打开报告可自动切换宽屏布局,关键词分组和独立展区均支持 Tab 快速切换,搜索框实时过滤新闻标题,邮件客户端仍显示原始窄屏布局,零回归
+- **暗色模式**:一键切换深色主题,自动记住偏好,适合夜间阅读
+- **一键复制新闻**:鼠标悬停新闻序号即可复制标题和链接,方便快速分享
+- **导出优化**:整页截图和分段截图合并为下拉式导出按钮,截图时自动还原干净布局
+- **快捷键系统**:支持 `W` 宽屏切换、`D` 暗色模式、`/` 搜索、`?` 查看快捷键提示
+- **阅读进度条**:页面顶部实时显示阅读进度
 
 ### 2026/02/09 - mcp-v4.0.0
 
@@ -267,6 +262,19 @@
 <details>
 <summary>👉 点击展开:<strong>历史更新</strong></summary>
 
+### 2026/03/12 - v6.5.0
+
+- **AI 智能筛选系统**:不用再手动设关键词!在 `ai_interests.txt` 里用日常语言写下你关注的方向(如"我想看 AI 和新能源相关新闻"),AI 会自动提取标签并对每条新闻打分,只推送真正和你相关的内容。万一 AI 筛选出了问题,会自动切回关键词匹配,推送不中断
+- **每个时段支持不同的筛选方式和关注方向**:Timeline 中的每个时间段现在可以独立设置用什么方式筛选、看什么类型的新闻。比如:早上用"科技关键词"快速过滤,晚上换成"金融 AI 兴趣描述"做深度筛选——同一个系统,不同时段看不同内容
+- **AI 分析范围独立于推送**:AI 分析的数据范围可以和推送内容不同。比如推送只发新增消息(避免重复打扰),但 AI 分析当天全部新闻(看完整趋势)。每个时段也能单独设置 AI 分析模式
+- **AI 筛选智能省钱**:已分析过的新闻不会重复消耗 token;兴趣描述修改后,AI 自动判断变化幅度——小改动只更新受影响的标签,大改动才全量重新分类
+- **多文件配置与标签隔离**:自定义关键词文件放 `config/custom/keyword/`,AI 兴趣文件放 `config/custom/ai/`,不同文件产生的标签各自独立、互不干扰
+- **AI 翻译精准控制**:可分别控制热榜、RSS、独立展示区是否翻译,没开启显示的区域自动跳过,不浪费 token
+- **远程存储批量上传**:多次写操作攒在一起统一提交云端,减少 API 调用次数
+- **每组关键词/标签展示数量限制**:通过 `max_news_per_keyword` 控制每个分组最多显示多少条新闻,避免单个热门话题占满整条推送
+- **时段冲突智能检测**:两个时间段如果有时间重叠,系统会自动报错提醒修改,避免配置冲突导致意外行为
+- 修复若干bug
+
 ### 2026/02/09 - v6.0.0
 
 > **Breaking Change**:配置文件升级(config.yaml 2.0.0),旧版 `push_window` 和 `analysis_window` 配置不再兼容,请参考新版 config.yaml 迁移
@@ -1095,6 +1103,19 @@ ai_translation:
 
 > ⚠️ 部分海外媒体内容可能涉及敏感话题,AI 模型可能拒绝翻译,建议根据实际需求筛选订阅源
 
+### **HTML 报告浏览器增强**(v6.6.0 新增)
+
+在浏览器中打开推送的 HTML 报告,自动解锁增强体验(邮件客户端不受影响):
+
+- **宽屏模式**:桌面端自动切换 1200px 宽屏布局,充分利用屏幕空间
+- **Tab 快速切换**:关键词分组和独立展区均支持 Tab 导航,告别长页面翻滚
+- **暗色模式**:一键切换深色主题,自动记住偏好
+- **实时搜索**:按 `/` 唤起搜索框,即时过滤新闻标题
+- **一键复制**:悬停新闻序号即可复制标题和链接
+- **快捷键**:`W` 宽屏、`D` 暗色、`/` 搜索、`?` 查看所有快捷键
+
+> 💡 所有增强功能基于渐进增强,邮件客户端仍显示原始 600px 布局,零回归
+
 ### **灵活存储架构**(v4.0.0 重大更新)
 
 **多存储后端支持**:

+ 1 - 1
pyproject.toml

@@ -1,6 +1,6 @@
 [project]
 name = "trendradar"
-version = "6.5.5"
+version = "6.6.0"
 description = "TrendRadar - 热点新闻聚合与分析工具"
 requires-python = ">=3.12"
 dependencies = [

+ 1 - 1
trendradar/__init__.py

@@ -9,5 +9,5 @@ TrendRadar - 热点新闻聚合与分析工具
 
 from trendradar.context import AppContext
 
-__version__ = "6.5.5"
+__version__ = "6.6.0"
 __all__ = ["AppContext", "__version__"]

+ 2 - 0
trendradar/__main__.py

@@ -564,6 +564,8 @@ class NewsAnalyzer:
                     scheduler = self.ctx.create_scheduler()
                     date_str = self.ctx.format_date()
                     scheduler.record_execution(schedule.period_key, "analyze", date_str)
+            elif result.skipped:
+                print(f"[AI] {result.error}")
             else:
                 print(f"[AI] 分析失败: {result.error}")
 

+ 3 - 1
trendradar/ai/analyzer.py

@@ -28,6 +28,7 @@ class AIAnalysisResult:
     # 基础元数据
     raw_response: str = ""               # 原始响应
     success: bool = False                # 是否成功
+    skipped: bool = False                # 是否因无内容跳过(非失败)
     error: str = ""                      # 错误信息
 
     # 新闻数量统计
@@ -139,7 +140,8 @@ class AIAnalyzer:
         if not news_content and not rss_content:
             return AIAnalysisResult(
                 success=False,
-                error="没有可分析的新闻内容",
+                skipped=True,
+                error="本轮无新增热点内容,跳过 AI 分析",
                 total_news=total_news,
                 hotlist_count=hotlist_total,
                 rss_count=rss_total,

+ 20 - 1
trendradar/ai/formatter.py

@@ -79,6 +79,8 @@ def _format_standalone_summaries(summaries: dict) -> str:
 def render_ai_analysis_markdown(result: AIAnalysisResult) -> str:
     """渲染为通用 Markdown 格式(Telegram、企业微信、ntfy、Bark、Slack)"""
     if not result.success:
+        if result.skipped:
+            return f"ℹ️ {result.error}"
         return f"⚠️ AI 分析失败: {result.error}"
 
     lines = ["**✨ AI 热点分析**", ""]
@@ -115,6 +117,8 @@ def render_ai_analysis_markdown(result: AIAnalysisResult) -> str:
 def render_ai_analysis_feishu(result: AIAnalysisResult) -> str:
     """渲染为飞书卡片 Markdown 格式"""
     if not result.success:
+        if result.skipped:
+            return f"ℹ️ {result.error}"
         return f"⚠️ AI 分析失败: {result.error}"
 
     lines = ["**✨ AI 热点分析**", ""]
@@ -151,6 +155,8 @@ def render_ai_analysis_feishu(result: AIAnalysisResult) -> str:
 def render_ai_analysis_dingtalk(result: AIAnalysisResult) -> str:
     """渲染为钉钉 Markdown 格式"""
     if not result.success:
+        if result.skipped:
+            return f"ℹ️ {result.error}"
         return f"⚠️ AI 分析失败: {result.error}"
 
     lines = ["### ✨ AI 热点分析", ""]
@@ -193,6 +199,8 @@ def render_ai_analysis_dingtalk(result: AIAnalysisResult) -> str:
 def render_ai_analysis_html(result: AIAnalysisResult) -> str:
     """渲染为 HTML 格式(邮件)"""
     if not result.success:
+        if result.skipped:
+            return f'<div class="ai-info">ℹ️ {_escape_html(result.error)}</div>'
         return (
             f'<div class="ai-error">⚠️ AI 分析失败: {_escape_html(result.error)}</div>'
         )
@@ -279,6 +287,8 @@ def render_ai_analysis_html(result: AIAnalysisResult) -> str:
 def render_ai_analysis_plain(result: AIAnalysisResult) -> str:
     """渲染为纯文本格式"""
     if not result.success:
+        if result.skipped:
+            return result.error
         return f"AI 分析失败: {result.error}"
 
     lines = ["【✨ AI 热点分析】", ""]
@@ -316,6 +326,8 @@ def render_ai_analysis_telegram(result: AIAnalysisResult) -> str:
     换行直接使用 \\n,不支持 <br>, <div>, <h1>-<h6> 等标签。
     """
     if not result.success:
+        if result.skipped:
+            return f"ℹ️ {_escape_html(result.error)}"
         return f"⚠️ AI 分析失败: {_escape_html(result.error)}"
 
     lines = ["<b>✨ AI 热点分析</b>", ""]
@@ -365,6 +377,11 @@ def render_ai_analysis_html_rich(result: AIAnalysisResult) -> str:
 
     # 检查是否成功
     if not result.success:
+        if result.skipped:
+            return f"""
+                <div class="ai-section">
+                    <div class="ai-info">ℹ️ {_escape_html(str(result.error))}</div>
+                </div>"""
         error_msg = result.error or "未知错误"
         return f"""
                 <div class="ai-section">
@@ -376,7 +393,8 @@ def render_ai_analysis_html_rich(result: AIAnalysisResult) -> str:
                     <div class="ai-section-header">
                         <div class="ai-section-title">✨ AI 热点分析</div>
                         <span class="ai-section-badge">AI</span>
-                    </div>"""
+                    </div>
+                    <div class="ai-blocks-grid">"""
 
     if result.core_trends:
         content = _format_list_content(result.core_trends)
@@ -434,5 +452,6 @@ def render_ai_analysis_html_rich(result: AIAnalysisResult) -> str:
                     </div>"""
 
     ai_html += """
+                    </div>
                 </div>"""
     return ai_html

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 842 - 15
trendradar/report/html.py


+ 1 - 1
uv.lock

@@ -1996,7 +1996,7 @@ wheels = [
 
 [[package]]
 name = "trendradar"
-version = "6.5.5"
+version = "6.6.0"
 source = { editable = "." }
 dependencies = [
     { name = "boto3" },

+ 1 - 1
version

@@ -1 +1 @@
-6.5.5
+6.6.0

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä