From 09da83b35ffa6bc82a45de9042c31a4b6fcae5cf Mon Sep 17 00:00:00 2001 From: WCY-dt <834421194@qq.com> Date: Sun, 6 Sep 2026 05:05:35 +0800 Subject: [PATCH 01/20] release: Update version to v1.3.30 --- CHANGELOG.md | 2 ++ README.md | 2 +- README_zh.md | 2 +- build/config.yml | 4 ++-- build/linux/Taskfile.yml | 2 +- build/linux/nfpm/nfpm.yaml | 2 +- docs/SERVER_MODE/swagger.json | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- internal/version/version.go | 2 +- main-core.go | 2 +- skills/mrrss-assistant/references/api.md | 2 +- website/package-lock.json | 4 ++-- website/package.json | 2 +- 14 files changed, 18 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f83a2a7..bedb4e25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ 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). +## [Unreleased] + ## [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 | 简体中文

-[![Version](https://img.shields.io/badge/version-1.3.29-blue.svg)](https://github.com/DevXDojo/MrRSS/releases) +[![Version](https://img.shields.io/badge/version-1.3.30-blue.svg)](https://github.com/DevXDojo/MrRSS/releases) [![License](https://img.shields.io/badge/license-GPLv3-green.svg)](LICENSE) [![Go](https://img.shields.io/badge/Go-1.27+-00ADD8?logo=go)](https://go.dev/) [![Wails](https://img.shields.io/badge/Wails-v3%20alpha-red)](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 | 简体中文

-[![Version](https://img.shields.io/badge/version-1.3.29-blue.svg)](https://github.com/DevXDojo/MrRSS/releases) +[![Version](https://img.shields.io/badge/version-1.3.30-blue.svg)](https://github.com/DevXDojo/MrRSS/releases) [![License](https://img.shields.io/badge/license-GPLv3-green.svg)](LICENSE) [![Go](https://img.shields.io/badge/Go-1.27+-00ADD8?logo=go)](https://go.dev/) [![Wails](https://img.shields.io/badge/Wails-v3%20alpha-red)](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/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/internal/version/version.go b/internal/version/version.go index 6fbef229..2b39d95f 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -1,4 +1,4 @@ package version // Version is the current application version -const Version = "1.3.29" +const Version = "1.3.30" diff --git a/main-core.go b/main-core.go index ece0faac..4116de03 100644 --- a/main-core.go +++ b/main-core.go @@ -31,7 +31,7 @@ import ( ) // @title MrRSS API -// @version 1.3.29 +// @version 1.3.30 // @description MrRSS is a modern, cross-platform desktop RSS reader with auto-translation, smart feed discovery, and AI-powered summarization. // @contact.name API Support diff --git a/skills/mrrss-assistant/references/api.md b/skills/mrrss-assistant/references/api.md index 626df98d..5dcc0b86 100644 --- a/skills/mrrss-assistant/references/api.md +++ b/skills/mrrss-assistant/references/api.md @@ -6,7 +6,7 @@ Generated from `docs/SERVER_MODE/swagger.json`. Regenerate with: python skills/mrrss-assistant/scripts/generate_api_reference.py docs/SERVER_MODE/swagger.json skills/mrrss-assistant/references/api.md ``` -- API version: `1.3.29` +- API version: `1.3.30` - API root: `{base_url}/api` - Endpoint paths below are relative to the API root unless they already start with `/api/`. diff --git a/website/package-lock.json b/website/package-lock.json index aa0b36be..ad16c580 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -1,12 +1,12 @@ { "name": "mrrss-website", - "version": "1.3.29", + "version": "1.3.30", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mrrss-website", - "version": "1.3.29", + "version": "1.3.30", "dependencies": { "@phosphor-icons/vue": "^2.2.1", "vue": "^3.5.42" diff --git a/website/package.json b/website/package.json index 9ab2e511..d5ca7fab 100644 --- a/website/package.json +++ b/website/package.json @@ -1,6 +1,6 @@ { "name": "mrrss-website", - "version": "1.3.29", + "version": "1.3.30", "description": "MrRSS - AI-Powered RSS Reader Landing Page", "type": "module", "scripts": { From 0ba95a84628cf090b05ce4a2ec30ae284c598d71 Mon Sep 17 00:00:00 2001 From: WCY-dt <834421194@qq.com> Date: Sun, 6 Sep 2026 05:12:34 +0800 Subject: [PATCH 02/20] fix(articles): preserve position after relative mark read (#1055) --- CHANGELOG.md | 4 ++ .../src/components/article/ArticleList.vue | 47 +++++++++++++++++-- .../composables/article/useArticleActions.ts | 21 ++++++--- 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bedb4e25..8d0993bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- 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/frontend/src/components/article/ArticleList.vue b/frontend/src/components/article/ArticleList.vue index 5f8dc48f..538b2b7c 100644 --- a/frontend/src/components/article/ArticleList.vue +++ b/frontend/src/components/article/ArticleList.vue @@ -200,10 +200,49 @@ function searchFieldLabel(field: 'title' | 'summary' | 'content'): string { return t(`aiSearch.matchFields.${field}`); } -const { showArticleContextMenu } = useArticleActions(t, defaultViewMode, async () => { - await store.fetchUnreadCounts(); - await store.fetchFilterCounts(); -}); +const { showArticleContextMenu } = useArticleActions( + t, + defaultViewMode, + async () => { + await store.fetchUnreadCounts(); + await store.fetchFilterCounts(); + }, + preserveRelativeReadPosition +); + +async function preserveRelativeReadPosition( + referenceArticle: Article, + direction: 'above' | 'below' +): Promise { + const list = listRef.value; + const anchor = list?.querySelector( + `[data-article-id="${referenceArticle.id}"]` + ); + const anchorTop = anchor?.getBoundingClientRect().top; + const referenceTime = new Date(referenceArticle.published_at).getTime(); + + if (Number.isFinite(referenceTime)) { + filteredArticles.value.forEach((article) => { + const publishedTime = new Date(article.published_at).getTime(); + if ( + Number.isFinite(publishedTime) && + (direction === 'above' ? publishedTime > referenceTime : publishedTime < referenceTime) + ) { + article.is_read = true; + } + }); + } + + await nextTick(); + if (list && anchorTop !== undefined) { + const updatedAnchor = list.querySelector( + `[data-article-id="${referenceArticle.id}"]` + ); + if (updatedAnchor) { + list.scrollTop += updatedAnchor.getBoundingClientRect().top - anchorTop; + } + } +} // Virtual rendering: only render visible articles + buffer const visibleArticles = computed(() => { diff --git a/frontend/src/composables/article/useArticleActions.ts b/frontend/src/composables/article/useArticleActions.ts index 0b3c92a7..5edaba8e 100644 --- a/frontend/src/composables/article/useArticleActions.ts +++ b/frontend/src/composables/article/useArticleActions.ts @@ -5,11 +5,16 @@ import type { Article } from '@/types/models'; import type { Composer } from 'vue-i18n'; type ViewMode = 'original' | 'rendered' | 'external'; +type RelativeReadDirection = 'above' | 'below'; export function useArticleActions( t: Composer['t'], defaultViewMode: { value: ViewMode }, - onReadStatusChange?: () => void + onReadStatusChange?: () => void | Promise, + onRelativeRead?: ( + article: Article, + direction: RelativeReadDirection + ) => void | Promise ) { const store = useAppStore(); @@ -221,7 +226,8 @@ export function useArticleActions( } } else if (action === 'markAboveAsRead' || action === 'markBelowAsRead') { try { - const direction = action === 'markAboveAsRead' ? 'above' : 'below'; + const direction: RelativeReadDirection = + action === 'markAboveAsRead' ? 'above' : 'below'; // Show confirmation dialog const confirmTitle = @@ -268,13 +274,14 @@ export function useArticleActions( const data = await res.json(); - // Refresh the article list to show updated read status - if (onReadStatusChange) { - onReadStatusChange(); + // Update the visible list in place so it never clears and loses its scroll position. + if (onRelativeRead) { + await onRelativeRead(article, direction); } - // Refresh articles from server - await store.fetchArticles(); + if (onReadStatusChange) { + await onReadStatusChange(); + } window.showToast( t('article.action.markedNArticlesAsRead', { count: data.count || 0 }), From 4d75096e3cdb71345a4af39f70d52abbdeb884df Mon Sep 17 00:00:00 2001 From: WCY-dt <834421194@qq.com> Date: Sun, 6 Sep 2026 05:12:53 +0800 Subject: [PATCH 03/20] fix(summary): collapse card height with content (#1056) --- CHANGELOG.md | 1 + frontend/src/components/article/parts/ArticleSummary.vue | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d0993bb..40ea9e33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- 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 diff --git a/frontend/src/components/article/parts/ArticleSummary.vue b/frontend/src/components/article/parts/ArticleSummary.vue index f5a16f2d..a75fd730 100644 --- a/frontend/src/components/article/parts/ArticleSummary.vue +++ b/frontend/src/components/article/parts/ArticleSummary.vue @@ -343,9 +343,7 @@ async function handleSummaryLinkClick(event: MouseEvent) { diff --git a/frontend/src/components/common/select.css b/frontend/src/components/common/select.css index c56b6fa5..efaee401 100644 --- a/frontend/src/components/common/select.css +++ b/frontend/src/components/common/select.css @@ -6,10 +6,11 @@ /* Trigger button - based on TagSelector and RuleConditionItem */ .select-trigger { - @apply w-full p-1.5 sm:p-2 border border-border rounded-md bg-bg-primary text-text-primary; + @apply w-full p-1.5 sm:p-2 border border-border bg-bg-primary text-text-primary; @apply flex items-center justify-between cursor-pointer hover:border-accent transition-colors; @apply text-xs sm:text-sm; - height: 38px; + height: var(--ui-control-height); + border-radius: var(--ui-radius-control); box-sizing: border-box; } @@ -33,8 +34,9 @@ /* Dropdown menu */ .select-dropdown { - @apply border border-border rounded-md bg-bg-primary; + @apply border border-border bg-bg-primary; @apply overflow-y-auto shadow-lg scroll-smooth; + border-radius: var(--ui-radius-surface); /* Position and dimensions are set via inline styles by useSelect */ } @@ -44,7 +46,7 @@ @apply text-text-primary hover:bg-bg-tertiary; @apply text-xs sm:text-sm; min-height: 36px; - transition: background-color 0.15s ease; + transition: background-color var(--ui-transition-fast); } .select-option:disabled { diff --git a/frontend/src/components/settings/base/SettingControl/ButtonControl.vue b/frontend/src/components/settings/base/SettingControl/ButtonControl.vue index 735341b4..472b7d13 100644 --- a/frontend/src/components/settings/base/SettingControl/ButtonControl.vue +++ b/frontend/src/components/settings/base/SettingControl/ButtonControl.vue @@ -38,7 +38,9 @@ async function handleClick() { diff --git a/frontend/src/components/settings/base/SubSettingItem.vue b/frontend/src/components/settings/base/SubSettingItem.vue index c53705ff..90815e93 100644 --- a/frontend/src/components/settings/base/SubSettingItem.vue +++ b/frontend/src/components/settings/base/SubSettingItem.vue @@ -50,6 +50,7 @@ withDefaults(defineProps(), { diff --git a/frontend/src/components/settings/base/TipBox.vue b/frontend/src/components/settings/base/TipBox.vue index 67bc9bbf..92381434 100644 --- a/frontend/src/components/settings/base/TipBox.vue +++ b/frontend/src/components/settings/base/TipBox.vue @@ -55,7 +55,8 @@ const boxClass = computed(() => {