diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f83a2a7..0cea7da8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,34 @@ All notable changes to MrRSS will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.3.30] - 2026-09-06 + +### Added + +- Add bilingual settings search with ranked, keyboard-accessible results that open the exact setting without hiding navigation. (#1067) +- Add an opt-out confirmation setting for bulk mark-as-read actions across article and sidebar menus. (#1063) +- Add a floating back-to-top button for long articles. (#1057) +- Show a live reading-progress bar while scrolling through an article. (#1058) + +### Changed + +- Replace the sidebar's boxed native sort field with a lightweight menu aligned with the surrounding navigation controls. (#1071) +- Unify shared button, form-control, dropdown, settings-card and keyboard-focus styling. (#1071) +- Standardize read and unread controls on hollow and filled circle icons across article views. (#1070) +- Show publication timestamps to minute precision when hovering article dates. (#1066) +- Let the up-to-date status card shrink to its content when no download action is present. (#1065) +- Replace the misleading empty unread-list error copy with a clear all-caught-up state. (#1064) +- Center article bodies at a readable 68-character line width with more consistent paragraph and list spacing. (#1059) + +### Fixed + +- Localize AI chat failures and feed update-status tooltips instead of exposing English backend values. (#1062) +- Keep AI feature toggles anchored while their dependent settings expand or collapse. (#1061) +- Use the theme's neutral primary text color consistently in AI profile forms and article chat. (#1060) +- Keep the article-summary heading stationary when generated content and action buttons appear. (#1068) +- Let collapsed article-summary cards shrink to their header instead of leaving a large blank area. (#1056) +- Keep the article list anchored at the selected item after marking newer or older articles as read. (#1055) + ## [1.3.29] - 2026-09-05 ### Added diff --git a/README.md b/README.md index 5d385244..967d5c19 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ English | 简体中文
-[](https://github.com/DevXDojo/MrRSS/releases) +[](https://github.com/DevXDojo/MrRSS/releases) [](LICENSE) [](https://go.dev/) [](https://wails.io/) diff --git a/README_zh.md b/README_zh.md index b0703394..0dd232bc 100644 --- a/README_zh.md +++ b/README_zh.md @@ -8,7 +8,7 @@ English | 简体中文 -[](https://github.com/DevXDojo/MrRSS/releases) +[](https://github.com/DevXDojo/MrRSS/releases) [](LICENSE) [](https://go.dev/) [](https://wails.io/) diff --git a/build/config.yml b/build/config.yml index 830fdeb1..9864c4bd 100644 --- a/build/config.yml +++ b/build/config.yml @@ -11,7 +11,7 @@ info: description: "A modern, standalone RSS reader" # The application description copyright: "Copyright © 2026" # Copyright text comments: "Built with Wails" # Comments - version: "1.3.29" # The application version + version: "1.3.30" # The application version # iOS build configuration (uncomment to customise iOS project generation) # Note: Keys under `ios` OVERRIDE values under `info` when set. @@ -21,7 +21,7 @@ info: # # The display name shown under the app icon (CFBundleDisplayName/CFBundleName) # displayName: "MrRSS" # # The app version to embed in Info.plist (CFBundleShortVersionString/CFBundleVersion) -# version: "1.3.29" +# version: "1.3.30" # # The company/organisation name for templates and project settings # company: "Ch3nyang" # # Additional comments to embed in Info.plist metadata diff --git a/build/linux/Taskfile.yml b/build/linux/Taskfile.yml index 41b5a7a9..4d9303b5 100644 --- a/build/linux/Taskfile.yml +++ b/build/linux/Taskfile.yml @@ -124,7 +124,7 @@ tasks: cmds: - cd {{.BIN_DIR}} && tar -czf {{.APP_NAME}}-{{.VERSION}}-linux-{{.ARCH}}.tar.gz {{.APP_NAME}} vars: - VERSION: '1.3.29' + VERSION: '1.3.30' ARCH: '{{.ARCH | default "amd64"}}' generate:dotdesktop: diff --git a/build/linux/nfpm/nfpm.yaml b/build/linux/nfpm/nfpm.yaml index d84f82a9..1a646920 100644 --- a/build/linux/nfpm/nfpm.yaml +++ b/build/linux/nfpm/nfpm.yaml @@ -6,7 +6,7 @@ name: "MrRSS" arch: ${GOARCH} platform: "linux" -version: "1.3.29" +version: "1.3.30" section: "default" priority: "extra" maintainer: ${GIT_COMMITTER_NAME} <${GIT_COMMITTER_EMAIL}> diff --git a/config/defaults.json b/config/defaults.json index 889e5b22..b7d193e8 100644 --- a/config/defaults.json +++ b/config/defaults.json @@ -18,6 +18,7 @@ "baidu_app_id": "", "baidu_secret_key": "", "close_to_tray": true, + "confirm_mark_as_read": true, "content_font_family": "system", "content_font_size": 16, "content_line_height": "1.6", diff --git a/docs/SERVER_MODE/swagger.json b/docs/SERVER_MODE/swagger.json index aca96928..385e6d9b 100644 --- a/docs/SERVER_MODE/swagger.json +++ b/docs/SERVER_MODE/swagger.json @@ -12,7 +12,7 @@ "name": "GPL-3.0", "url": "https://www.gnu.org/licenses/gpl-3.0.en.html" }, - "version": "1.3.29" + "version": "1.3.30" }, "host": "localhost:1234", "basePath": "/api", diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5b38c215..411325b9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "mrrss-frontend", - "version": "1.3.29", + "version": "1.3.30", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mrrss-frontend", - "version": "1.3.29", + "version": "1.3.30", "dependencies": { "@eslint/js": "^10.0.1", "@phosphor-icons/vue": "^2.2.1", diff --git a/frontend/package.json b/frontend/package.json index c8af3212..77229eeb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "mrrss-frontend", "private": true, - "version": "1.3.29", + "version": "1.3.30", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/components/article/ArticleCardItem.vue b/frontend/src/components/article/ArticleCardItem.vue index 10738cb0..8a62616d 100644 --- a/frontend/src/components/article/ArticleCardItem.vue +++ b/frontend/src/components/article/ArticleCardItem.vue @@ -4,7 +4,7 @@ import { ref, computed, onMounted, onBeforeUnmount } from 'vue'; import { useI18n } from 'vue-i18n'; import { PhEyeSlash, PhStar, PhClockCountdown } from '@phosphor-icons/vue'; import type { Article } from '@/types/models'; -import { formatDate as formatDateUtil } from '@/utils/date'; +import { formatDate as formatDateUtil, formatExactDateTime } from '@/utils/date'; import { getProxiedMediaUrl, isMediaCacheEnabled } from '@/utils/mediaProxy'; import { useAppStore } from '@/stores/app'; import { imageCache } from '@/utils/imageCache'; @@ -205,7 +205,9 @@ function handleImageError(event: Event) { diff --git a/frontend/src/components/article/ArticleChatPanel.vue b/frontend/src/components/article/ArticleChatPanel.vue index bee2d2ed..8cbf37a3 100644 --- a/frontend/src/components/article/ArticleChatPanel.vue +++ b/frontend/src/components/article/ArticleChatPanel.vue @@ -13,6 +13,7 @@ import { PhPencil, } from '@phosphor-icons/vue'; import type { Article } from '@/types/models'; +import { readAIError } from '@/utils/aiError'; interface ChatMessage { id: number; @@ -292,36 +293,20 @@ async function sendMessage() { isFirstMessage.value = false; } else { - const errorText = await response.text(); console.error('AI chat request failed:', response.status); - - let errorMessage = t('article.chat.aiChatError'); - let persistedSessionID = 0; - try { - const errorData = JSON.parse(errorText); - persistedSessionID = Number(errorData.session_id || 0); - // Extract error message from various possible formats - if (typeof errorData.error === 'string') { - errorMessage = errorData.error; - } else if (errorData.error?.message) { - errorMessage = errorData.error.message; - } else if (errorData.message) { - errorMessage = errorData.message; - } else if (typeof errorData === 'string') { - errorMessage = errorData; - } else { - errorMessage = t('article.chat.aiChatError'); - } - } catch { - errorMessage = t('article.chat.aiChatError'); - } + const chatError = await readAIError(response); + const errorPayload = + chatError.payload !== null && typeof chatError.payload === 'object' + ? (chatError.payload as Record