This commit is contained in:
AmintaCCCP
2025-08-12 20:48:52 +08:00
parent 4ef03f9dec
commit 3372552391
9 changed files with 103 additions and 29 deletions

4
dist/index.html vendored
View File

@@ -10,8 +10,8 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<!-- Material Icons CDN --> <!-- Material Icons CDN -->
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" /> <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<script type="module" crossorigin src="/assets/index-CQ4S0AeE.js"></script> <script type="module" crossorigin src="/assets/index-DZXyNObJ.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BQKjD-gi.css"> <link rel="stylesheet" crossorigin href="/assets/index-BWYOfSoc.css">
</head> </head>
<body class="bg-gray-50 dark:bg-gray-900"> <body class="bg-gray-50 dark:bg-gray-900">
<div id="root"></div> <div id="root"></div>

View File

@@ -1,7 +1,7 @@
{ {
"name": "github-stars-manager", "name": "github-stars-manager",
"private": true, "private": true,
"version": "0.1.3", "version": "0.1.4",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -197,7 +197,8 @@ export const RepositoryCard: React.FC<RepositoryCardProps> = ({
ai_summary: analysis.summary, ai_summary: analysis.summary,
ai_tags: analysis.tags, ai_tags: analysis.tags,
ai_platforms: analysis.platforms, ai_platforms: analysis.platforms,
analyzed_at: new Date().toISOString() analyzed_at: new Date().toISOString(),
analysis_failed: false // 分析成功,清除失败标记
}; };
updateRepository(updatedRepo); updateRepository(updatedRepo);
@@ -209,6 +210,16 @@ export const RepositoryCard: React.FC<RepositoryCardProps> = ({
alert(successMessage); alert(successMessage);
} catch (error) { } catch (error) {
console.error('AI analysis failed:', error); console.error('AI analysis failed:', error);
// 标记为分析失败
const failedRepo = {
...repository,
analyzed_at: new Date().toISOString(),
analysis_failed: true
};
updateRepository(failedRepo);
alert(language === 'zh' ? 'AI分析失败请检查AI配置和网络连接。' : 'AI analysis failed. Please check AI configuration and network connection.'); alert(language === 'zh' ? 'AI分析失败请检查AI配置和网络连接。' : 'AI analysis failed. Please check AI configuration and network connection.');
} finally { } finally {
setLoading(false); setLoading(false);
@@ -232,6 +243,12 @@ export const RepositoryCard: React.FC<RepositoryCardProps> = ({
content: repository.custom_description, content: repository.custom_description,
isCustom: true isCustom: true
}; };
} else if (showAISummary && repository.analysis_failed) {
return {
content: repository.description || (language === 'zh' ? '暂无描述' : 'No description available'),
isAI: false,
isFailed: true
};
} else if (showAISummary && repository.ai_summary) { } else if (showAISummary && repository.ai_summary) {
return { return {
content: repository.ai_summary, content: repository.ai_summary,
@@ -277,7 +294,12 @@ export const RepositoryCard: React.FC<RepositoryCardProps> = ({
// 获取AI分析按钮的提示文本 // 获取AI分析按钮的提示文本
const getAIButtonTitle = () => { const getAIButtonTitle = () => {
if (repository.analyzed_at) { if (repository.analysis_failed) {
const analyzeTime = new Date(repository.analyzed_at!).toLocaleString();
return language === 'zh'
? `分析失败于 ${analyzeTime},点击重新分析`
: `Analysis failed on ${analyzeTime}, click to retry`;
} else if (repository.analyzed_at) {
const analyzeTime = new Date(repository.analyzed_at).toLocaleString(); const analyzeTime = new Date(repository.analyzed_at).toLocaleString();
return language === 'zh' return language === 'zh'
? `已于 ${analyzeTime} 分析过,点击重新分析` ? `已于 ${analyzeTime} 分析过,点击重新分析`
@@ -315,7 +337,10 @@ export const RepositoryCard: React.FC<RepositoryCardProps> = ({
<button <button
onClick={handleAIAnalyze} onClick={handleAIAnalyze}
disabled={isLoading} disabled={isLoading}
className={`p-2 rounded-lg transition-colors ${repository.analyzed_at className={`p-2 rounded-lg transition-colors ${
repository.analysis_failed
? 'bg-red-100 text-red-600 dark:bg-red-900 dark:text-red-400 hover:bg-red-200 dark:hover:bg-red-800'
: repository.analyzed_at
? 'bg-green-100 text-green-600 dark:bg-green-900 dark:text-green-400 hover:bg-green-200 dark:hover:bg-green-800' ? 'bg-green-100 text-green-600 dark:bg-green-900 dark:text-green-400 hover:bg-green-200 dark:hover:bg-green-800'
: 'bg-purple-100 text-purple-600 dark:bg-purple-900 dark:text-purple-400 hover:bg-purple-200 dark:hover:bg-purple-800' : 'bg-purple-100 text-purple-600 dark:bg-purple-900 dark:text-purple-400 hover:bg-purple-200 dark:hover:bg-purple-800'
} disabled:opacity-50`} } disabled:opacity-50`}
@@ -398,7 +423,13 @@ export const RepositoryCard: React.FC<RepositoryCardProps> = ({
<span>{language === 'zh' ? '自定义' : 'Custom'}</span> <span>{language === 'zh' ? '自定义' : 'Custom'}</span>
</div> </div>
)} )}
{displayContent.isAI && ( {displayContent.isFailed && (
<div className="flex items-center space-x-1 text-xs text-red-600 dark:text-red-400">
<Bot className="w-3 h-3" />
<span>{language === 'zh' ? '分析失败' : 'Analysis Failed'}</span>
</div>
)}
{displayContent.isAI && !displayContent.isFailed && (
<div className="flex items-center space-x-1 text-xs text-green-600 dark:text-green-400"> <div className="flex items-center space-x-1 text-xs text-green-600 dark:text-green-400">
<Bot className="w-3 h-3" /> <Bot className="w-3 h-3" />
<span>{language === 'zh' ? 'AI总结' : 'AI Summary'}</span> <span>{language === 'zh' ? 'AI总结' : 'AI Summary'}</span>
@@ -494,7 +525,12 @@ export const RepositoryCard: React.FC<RepositoryCardProps> = ({
<span>{language === 'zh' ? '已编辑' : 'Edited'}</span> <span>{language === 'zh' ? '已编辑' : 'Edited'}</span>
</div> </div>
)} )}
{repository.analyzed_at && ( {repository.analysis_failed ? (
<div className="flex items-center space-x-1 text-xs">
<div className="w-2 h-2 bg-red-500 rounded-full" />
<span>{language === 'zh' ? '分析失败' : 'Analysis failed'}</span>
</div>
) : repository.analyzed_at && (
<div className="flex items-center space-x-1 text-xs"> <div className="flex items-center space-x-1 text-xs">
<div className="w-2 h-2 bg-green-500 rounded-full" /> <div className="w-2 h-2 bg-green-500 rounded-full" />
<span>{language === 'zh' ? 'AI已分析' : 'AI analyzed'}</span> <span>{language === 'zh' ? 'AI已分析' : 'AI analyzed'}</span>

View File

@@ -75,7 +75,7 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
); );
}); });
const handleAIAnalyze = async (analyzeUnanalyzedOnly: boolean = false) => { const handleAIAnalyze = async (analyzeUnanalyzedOnly: boolean = false, analyzeFailedOnly: boolean = false) => {
if (!githubToken) { if (!githubToken) {
alert(language === 'zh' ? 'GitHub token 未找到,请重新登录。' : 'GitHub token not found. Please login again.'); alert(language === 'zh' ? 'GitHub token 未找到,请重新登录。' : 'GitHub token not found. Please login again.');
return; return;
@@ -87,21 +87,27 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
return; return;
} }
const targetRepos = analyzeUnanalyzedOnly const targetRepos = analyzeFailedOnly
? filteredRepositories.filter(repo => repo.analysis_failed)
: analyzeUnanalyzedOnly
? filteredRepositories.filter(repo => !repo.analyzed_at) ? filteredRepositories.filter(repo => !repo.analyzed_at)
: filteredRepositories; : filteredRepositories;
if (targetRepos.length === 0) { if (targetRepos.length === 0) {
alert(language === 'zh' const message = analyzeFailedOnly
? (analyzeUnanalyzedOnly ? '所有仓库都已经分析过了!' : '没有分析的仓库!') ? (language === 'zh' ? '没有分析失败的仓库!' : 'No failed repositories to re-analyze!')
: (analyzeUnanalyzedOnly ? 'All repositories have been analyzed!' : 'No repositories to analyze!') : analyzeUnanalyzedOnly
); ? (language === 'zh' ? '所有仓库都已经分析过了!' : 'All repositories have been analyzed!')
: (language === 'zh' ? '没有可分析的仓库!' : 'No repositories to analyze!');
alert(message);
return; return;
} }
const actionText = language === 'zh' const actionText = analyzeFailedOnly
? (analyzeUnanalyzedOnly ? '未分析' : '全部') ? (language === 'zh' ? '失败' : 'failed')
: (analyzeUnanalyzedOnly ? 'unanalyzed' : 'all'); : analyzeUnanalyzedOnly
? (language === 'zh' ? '未分析' : 'unanalyzed')
: (language === 'zh' ? '全部' : 'all');
const confirmMessage = language === 'zh' const confirmMessage = language === 'zh'
? `将对 ${targetRepos.length}${actionText}仓库进行AI分析这可能需要几分钟时间。是否继续` ? `将对 ${targetRepos.length}${actionText}仓库进行AI分析这可能需要几分钟时间。是否继续`
@@ -159,7 +165,8 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
ai_summary: analysis.summary, ai_summary: analysis.summary,
ai_tags: analysis.tags, ai_tags: analysis.tags,
ai_platforms: analysis.platforms, ai_platforms: analysis.platforms,
analyzed_at: new Date().toISOString() analyzed_at: new Date().toISOString(),
analysis_failed: false // 分析成功,清除失败标记
}; };
updateRepository(updatedRepo); updateRepository(updatedRepo);
@@ -169,6 +176,18 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
return true; return true;
} catch (error) { } catch (error) {
console.warn(`Failed to analyze ${repo.full_name}:`, error); console.warn(`Failed to analyze ${repo.full_name}:`, error);
// 标记为分析失败
const failedRepo = {
...repo,
analyzed_at: new Date().toISOString(),
analysis_failed: true
};
updateRepository(failedRepo);
analyzed++;
setAnalysisProgress({ current: analyzed, total: targetRepos.length });
return false; return false;
} }
}; };
@@ -275,7 +294,8 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
} }
const unanalyzedCount = filteredRepositories.filter(r => !r.analyzed_at).length; const unanalyzedCount = filteredRepositories.filter(r => !r.analyzed_at).length;
const analyzedCount = filteredRepositories.filter(r => r.analyzed_at).length; const analyzedCount = filteredRepositories.filter(r => r.analyzed_at && !r.analysis_failed).length;
const failedCount = filteredRepositories.filter(r => r.analysis_failed).length;
const t = (zh: string, en: string) => language === 'zh' ? zh : en; const t = (zh: string, en: string) => language === 'zh' ? zh : en;
@@ -320,7 +340,7 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
<button <button
onClick={() => handleAIAnalyze(true)} onClick={() => handleAIAnalyze(true)}
disabled={unanalyzedCount === 0} disabled={unanalyzedCount === 0}
className="w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" className="w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed border-b border-gray-100 dark:border-gray-600"
> >
<div className="font-medium text-gray-900 dark:text-white"> <div className="font-medium text-gray-900 dark:text-white">
{t('分析未分析的', 'Analyze Unanalyzed')} {t('分析未分析的', 'Analyze Unanalyzed')}
@@ -329,6 +349,18 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
{t(`分析 ${unanalyzedCount} 个未分析仓库`, `Analyze ${unanalyzedCount} unanalyzed repositories`)} {t(`分析 ${unanalyzedCount} 个未分析仓库`, `Analyze ${unanalyzedCount} unanalyzed repositories`)}
</div> </div>
</button> </button>
<button
onClick={() => handleAIAnalyze(false, true)}
disabled={failedCount === 0}
className="w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
<div className="font-medium text-gray-900 dark:text-white">
{t('重新分析失败的', 'Re-analyze Failed')}
</div>
<div className="text-sm text-gray-500 dark:text-gray-400">
{t(`重新分析 ${failedCount} 个失败仓库`, `Re-analyze ${failedCount} failed repositories`)}
</div>
</button>
</div> </div>
)} )}
</div> </div>
@@ -414,6 +446,11 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
{analyzedCount} {t('个已AI分析', 'AI analyzed')} {analyzedCount} {t('个已AI分析', 'AI analyzed')}
</span> </span>
)} )}
{failedCount > 0 && (
<span className="mr-3">
{failedCount} {t('个分析失败', 'analysis failed')}
</span>
)}
{unanalyzedCount > 0 && ( {unanalyzedCount > 0 && (
<span> <span>
{unanalyzedCount} {t('个未分析', 'unanalyzed')} {unanalyzedCount} {t('个未分析', 'unanalyzed')}

View File

@@ -373,7 +373,7 @@ Focus on practicality and accurate categorization to help users quickly understa
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-1"> <p className="text-sm text-gray-600 dark:text-gray-400 mb-1">
{t('当前版本: v0.1.3', 'Current Version: v0.1.3')} {t('当前版本: v0.1.4', 'Current Version: v0.1.4')}
</p> </p>
<p className="text-xs text-gray-500 dark:text-gray-500"> <p className="text-xs text-gray-500 dark:text-gray-500">
{t('检查是否有新版本可用', 'Check if a new version is available')} {t('检查是否有新版本可用', 'Check if a new version is available')}

View File

@@ -58,8 +58,8 @@ export class AIService {
return this.parseAIResponse(content); return this.parseAIResponse(content);
} catch (error) { } catch (error) {
console.error('AI analysis failed:', error); console.error('AI analysis failed:', error);
// Fallback to basic analysis // 抛出错误,让调用方处理失败状态
return this.fallbackAnalysis(repository); throw error;
} }
} }

View File

@@ -17,7 +17,7 @@ export class UpdateService {
private static getCurrentVersion(): string { private static getCurrentVersion(): string {
// 在实际应用中,这个版本号应该在构建时注入 // 在实际应用中,这个版本号应该在构建时注入
// 这里暂时硬编码,你可以通过构建脚本或环境变量来动态设置 // 这里暂时硬编码,你可以通过构建脚本或环境变量来动态设置
return '0.1.3'; return '0.1.4';
} }
static async checkForUpdates(): Promise<UpdateCheckResult> { static async checkForUpdates(): Promise<UpdateCheckResult> {

View File

@@ -20,6 +20,7 @@ export interface Repository {
ai_tags?: string[]; ai_tags?: string[];
ai_platforms?: string[]; // 新增:支持的平台类型 ai_platforms?: string[]; // 新增:支持的平台类型
analyzed_at?: string; analyzed_at?: string;
analysis_failed?: boolean; // 新增AI分析是否失败
// Release subscription // Release subscription
subscribed_to_releases?: boolean; subscribed_to_releases?: boolean;
// Manual editing fields // Manual editing fields

View File

@@ -64,7 +64,7 @@
<h1>GitHub Stars Manager - 更新功能测试</h1> <h1>GitHub Stars Manager - 更新功能测试</h1>
<div> <div>
<h2>当前版本: v0.1.3</h2> <h2>当前版本: v0.1.4</h2>
<button id="checkUpdate" onclick="checkForUpdates()">检查更新</button> <button id="checkUpdate" onclick="checkForUpdates()">检查更新</button>
<div id="status"></div> <div id="status"></div>
<div id="updateInfo"></div> <div id="updateInfo"></div>