From 147d44f957fbe8301ff0654923e477c37e4063b9 Mon Sep 17 00:00:00 2001
From: AmintaCCCP
Date: Mon, 27 Apr 2026 22:28:24 +0800
Subject: [PATCH 1/5] chore: add v0.5.5 to version-info.xml
Co-Authored-By: Claude Opus 4.7
---
versions/version-info.xml | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/versions/version-info.xml b/versions/version-info.xml
index f2cf8ec5..74ffa680 100644
--- a/versions/version-info.xml
+++ b/versions/version-info.xml
@@ -279,4 +279,19 @@
https://github.com/AmintaCCCP/GithubStarsManager/releases/tag/v0.5.4
+
+ 0.5.5
+ 2026-04-27
+
+ - Update Dialog Portal Fix: Fixed the update dialog backdrop mask being trapped inside the overflow container. The dialog now renders via React Portal at the document body level with proper z-index stacking. Added click-on-mask-to-dismiss behavior for better UX. (PR #112)
+ - Banner Layout Improvements: Fixed banner button wrapping on narrow viewports. Removed invalid Tailwind classes and improved responsive layout with proper flex behavior. (PR #112)
+ - Electron Focus Hijacking Fix: Replaced native alert()/confirm() dialogs with custom React components (Toast and ConfirmDialog), resolving focus trapping issues in Electron. (PR #117)
+ - Category Lock Toggle Visibility: Improved the category lock toggle color differentiation in dark mode. Unlocked state now uses gray-600, locked state uses amber. Both states are clearly distinguishable. (PR #118)
+ - AI Connection Test Enhancements: AI connection test now returns detailed, user-facing results including status and guidance messages. (PR #114)
+ - AI Configuration Error Handling: AI analysis now properly blocks execution when API keys are empty or cannot be decrypted. Bulk AI config import now skips entries without usable keys and logs reasons. (PR #114)
+ - Unified Error Messages and UI Polish: Consolidated AI configuration error messages and improved markdown image/GitHub link dark-mode styling. (PR #114)
+ - Special thanks to @SummerRay160 for their outstanding contributions to this release!
+
+ https://github.com/AmintaCCCP/GithubStarsManager/releases/tag/v0.5.5
+
From cf2c6359b8d9d448affaeb2883996749899dd9c9 Mon Sep 17 00:00:00 2001
From: AmintaCCCP
Date: Thu, 30 Apr 2026 17:17:47 +0800
Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=E5=AE=8C=E5=96=84=20Release=20?=
=?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=9C=BA=E5=88=B6=20(#119)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
主要改动:
- 移除 Header 同步按钮的 Release 获取逻辑,仅同步仓库列表
- 重构 getMultipleRepositoryReleases 支持增量同步和 pre-release 过滤
- 新增 fetchAllReleasesForRepo 分页拉取最多30条
- 添加速率限制检测,剩余配额不足时自动等待重置
- 支持 includePreRelease 开关,默认开启,状态持久化
- 老用户迁移:已有 release 数据的仓库标记为已同步,走增量
- 刷新失败仓库汇总提示,不再静默跳过
Co-Authored-By: Claude Opus 4.7
---
src/components/Header.tsx | 8 +-
src/components/ReleaseTimeline.tsx | 101 +++++++++-----
src/services/githubApi.ts | 217 +++++++++++++++++++++++------
src/store/useAppStore.ts | 26 +++-
src/types/index.ts | 4 +
5 files changed, 273 insertions(+), 83 deletions(-)
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index fff25ced..6eed0ebd 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -99,10 +99,8 @@ export const Header: React.FC = () => {
setRepositories(mergedRepositories);
- // 3. 获取Release信息
- console.log('Fetching releases...');
- const releases = await githubApi.getMultipleRepositoryReleases(mergedRepositories.slice(0, 20));
- setReleases(releases);
+ // Note: Release fetching is now handled by the Refresh button in Release Timeline
+ // Header sync only syncs the starred repos list
setLastSync(new Date().toISOString());
console.log('Sync completed successfully');
@@ -367,7 +365,7 @@ export const Header: React.FC = () => {
onClick={handleSync}
disabled={isLoading}
className="p-1 rounded hover:bg-light-surface dark:hover:bg-white/5 transition-colors disabled:opacity-50"
- title={t('同步仓库', 'Sync repositories')}
+ title={t('同步星标仓库列表(不包含Release)', 'Sync starred repos list (excludes Release)')}
>
diff --git a/src/components/ReleaseTimeline.tsx b/src/components/ReleaseTimeline.tsx
index 37cccb4e..6fd3dc94 100644
--- a/src/components/ReleaseTimeline.tsx
+++ b/src/components/ReleaseTimeline.tsx
@@ -36,6 +36,8 @@ export const ReleaseTimeline: React.FC = () => {
setReleaseSearchQuery,
toggleReleaseExpandedRepository,
setReleaseIsRefreshing,
+ includePreRelease,
+ setIncludePreRelease,
} = useAppStore();
const { toast, confirm } = useDialog();
@@ -301,6 +303,7 @@ export const ReleaseTimeline: React.FC = () => {
setReleaseIsRefreshing(true);
try {
const githubApi = new GitHubApiService(githubToken);
+ // Only fetch releases for repos that are subscribed to releases
const subscribedRepos = repositories.filter(repo => releaseSubscriptions.has(repo.id));
if (subscribedRepos.length === 0) {
@@ -308,49 +311,46 @@ export const ReleaseTimeline: React.FC = () => {
return;
}
- const allNewReleases: Release[] = [];
-
- const latestReleaseTime = releases.length > 0
- ? releases.reduce((max, r) => Math.max(max, new Date(r.published_at).getTime()), 0)
- : 0;
- const sinceTimestamp = latestReleaseTime > 0 ? new Date(latestReleaseTime).toISOString() : undefined;
+ // Use the new getMultipleRepositoryReleases with options
+ const { releases: newReleases, failedRepos } = await githubApi.getMultipleRepositoryReleases(
+ subscribedRepos,
+ { includePreRelease }
+ );
+ // Update repository sync metadata
+ const now = new Date().toISOString();
for (const repo of subscribedRepos) {
- const [owner, name] = repo.full_name.split('/');
-
- const hasExistingReleases = releases.some(r => r.repository.id === repo.id);
-
- let repoReleases: Release[];
- if (!hasExistingReleases) {
- repoReleases = await githubApi.getRepositoryReleases(owner, name, 1, 10);
- } else {
- repoReleases = await githubApi.getIncrementalRepositoryReleases(owner, name, sinceTimestamp, 10);
- }
-
- repoReleases.forEach(release => {
- release.repository.id = repo.id;
+ updateRepository({
+ ...repo,
+ has_fetched_releases: true,
+ last_release_fetch_time: now,
});
-
- allNewReleases.push(...repoReleases);
-
- await new Promise(resolve => setTimeout(resolve, 200));
}
+ // Filter out existing releases and add new ones
const existingIds = new Set(useAppStore.getState().releases.map(r => r.id));
- const actuallyNewCount = allNewReleases.filter(r => !existingIds.has(r.id)).length;
+ const actuallyNewReleases = newReleases.filter(r => !existingIds.has(r.id));
+ const actuallyNewCount = actuallyNewReleases.length;
- if (allNewReleases.length > 0) {
- addReleases(allNewReleases);
+ if (actuallyNewReleases.length > 0) {
+ addReleases(actuallyNewReleases);
}
- const now = new Date().toISOString();
setLastRefreshTime(now);
- const message = language === 'zh'
- ? `刷新完成!发现 ${actuallyNewCount} 个新Release。`
- : `Refresh completed! Found ${actuallyNewCount} new releases.`;
+ // Build success message with failed repos info
+ let message: string;
+ if (failedRepos.length > 0) {
+ message = language === 'zh'
+ ? `刷新完成!发现 ${actuallyNewCount} 个新Release,${failedRepos.length} 个仓库刷新失败。`
+ : `Refresh completed! Found ${actuallyNewCount} new releases, ${failedRepos.length} repos failed.`;
+ } else {
+ message = language === 'zh'
+ ? `刷新完成!发现 ${actuallyNewCount} 个新Release。`
+ : `Refresh completed! Found ${actuallyNewCount} new releases.`;
+ }
- toast(message, 'success');
+ toast(message, actuallyNewCount > 0 ? 'success' : 'info');
} catch (error) {
console.error('Refresh failed:', error);
const errorMessage = language === 'zh'
@@ -527,19 +527,35 @@ export const ReleaseTimeline: React.FC = () => {
}
- {/* 刷新按钮 - 在有订阅仓库时显示 */}
+ {/* Pre-release toggle + Refresh button */}
{subscribedRepoCount > 0 && (
-
+
+ {/* Pre-release toggle */}
+
+
+ {/* Refresh button */}
{lastRefreshTime && (
-
+
{t('上次刷新:', 'Last refresh:')} {formatDistanceToNow(new Date(lastRefreshTime), { addSuffix: true })}
)}
@@ -598,6 +614,21 @@ export const ReleaseTimeline: React.FC = () => {
)}
+ {/* Pre-release toggle */}
+
+
{/* Refresh Button */}