Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/png" href="/icon.png" />
<link rel="apple-touch-icon" href="/icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GitHub Stars Manager - AI-Powered Repository Management</title>
<meta name="description" content="Intelligent management of your GitHub starred repositories with AI-powered analysis and release tracking" />
Expand All @@ -15,4 +16,4 @@
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
</html>
6 changes: 4 additions & 2 deletions nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ events {
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
resolver 127.0.0.11 ipv6=off valid=30s;

# Hide nginx version
server_tokens off;
Expand Down Expand Up @@ -32,7 +33,8 @@ http {
server_name localhost;
# Backend API proxy
location /api/ {
proxy_pass http://backend:3000/api/;
set $backend_upstream backend:3000;
proxy_pass http://$backend_upstream;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
Expand Down Expand Up @@ -86,4 +88,4 @@ http {
internal;
}
}
}
}
8 changes: 4 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect } from 'react';
import { LoginScreen } from './components/LoginScreen';
import { Header } from './components/Header';
import { SearchBar } from './components/SearchBar';
Expand All @@ -16,13 +16,13 @@ function App() {
const {
isAuthenticated,
currentView,
selectedCategory,
theme,
searchResults,
repositories
repositories,
setSelectedCategory,
} = useAppStore();

const [selectedCategory, setSelectedCategory] = useState('all');

// 自动检查更新
useAutoUpdateCheck();

Expand Down
12 changes: 8 additions & 4 deletions src/components/LoginScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Star, Github, Key, ArrowRight, AlertCircle } from 'lucide-react';
import { Github, Key, ArrowRight, AlertCircle } from 'lucide-react';
import { useAppStore } from '../store/useAppStore';
import { GitHubApiService } from '../services/githubApi';

Expand Down Expand Up @@ -67,8 +67,12 @@ export const LoginScreen: React.FC = () => {
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
<div className="max-w-md w-full">
<div className="text-center mb-8">
<div className="flex items-center justify-center w-16 h-16 bg-blue-600 rounded-2xl mx-auto mb-4 shadow-lg">
<Star className="w-8 h-8 text-white" />
<div className="flex items-center justify-center w-16 h-16 bg-white rounded-2xl mx-auto mb-4 shadow-lg ring-1 ring-blue-100 overflow-hidden">
<img
src="./icon.png"
alt="GitHub Stars Manager"
Comment on lines +70 to +73
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify where icon.png actually exists and how it's referenced.
fd -a 'icon\.png$'
rg -n --type=tsx 'src="\./icon\.png"'
rg -n --type=tsx "from ['\"].*icon\.png['\"]"

Repository: AmintaCCCP/GithubStarsManager

Length of output: 187


🏁 Script executed:

cd /home/jailuser/git && find . -name "LoginScreen.tsx" -type f

Repository: AmintaCCCP/GithubStarsManager

Length of output: 105


🏁 Script executed:

cd /home/jailuser/git && head -100 src/components/LoginScreen.tsx | tail -40

Repository: AmintaCCCP/GithubStarsManager

Length of output: 1791


🏁 Script executed:

cd /home/jailuser/git && sed -n '1,10p' src/components/LoginScreen.tsx

Repository: AmintaCCCP/GithubStarsManager

Length of output: 562


🏁 Script executed:

cd /home/jailuser/git && sed -n '65,80p' src/components/LoginScreen.tsx

Repository: AmintaCCCP/GithubStarsManager

Length of output: 772


🏁 Script executed:

cd /home/jailuser/git && rg 'icon\.png' --no-filename

Repository: AmintaCCCP/GithubStarsManager

Length of output: 250


🏁 Script executed:

cd /home/jailuser/git && find . -maxdepth 2 -name "vite.config.*" -o -name "webpack.config.*" -o -name "tsconfig.json" -o -name "package.json" | head -10

Repository: AmintaCCCP/GithubStarsManager

Length of output: 165


🏁 Script executed:

cd /home/jailuser/git && cat package.json | grep -A 5 -B 5 "build\|vite\|webpack" | head -30

Repository: AmintaCCCP/GithubStarsManager

Length of output: 1034


🏁 Script executed:

cd /home/jailuser/git && ls -la assets/ public/ 2>/dev/null | head -20

Repository: AmintaCCCP/GithubStarsManager

Length of output: 521


🏁 Script executed:

cd /home/jailuser/git && rg "from.*assets" src/ --type tsx | head -10

Repository: AmintaCCCP/GithubStarsManager

Length of output: 100


Import the icon as a bundled asset instead of using a relative string path.

Line 72 uses src="./icon.png", which is fragile in routed paths. In a Vite app, relative paths resolve to the current route location, not the project root. Since icon.png exists in src/assets/, import it as a module and bind src to that URL for proper bundling and cache-busting.

Suggested fix
+import icon from '../assets/icon.png';
...
            <img
-             src="./icon.png"
+             src={icon}
              alt="GitHub Stars Manager"
              className="w-full h-full object-cover"
            />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/LoginScreen.tsx` around lines 70 - 73, In LoginScreen.tsx
replace the fragile string path on the img element by importing the asset as a
module (e.g., import icon from 'src/assets/icon.png') and bind the imported
variable to the img src (the <img ... /> in the JSX) so Vite will bundle and
cache-bust the asset; update the top of the file to add the import and change
the img src attribute to use the imported identifier.

className="w-full h-full object-cover"
/>
</div>
<h1 className="text-3xl font-bold text-gray-900 mb-2">
GitHub Stars Manager
Expand Down Expand Up @@ -177,4 +181,4 @@ export const LoginScreen: React.FC = () => {
</div>
</div>
);
};
};
83 changes: 77 additions & 6 deletions src/components/RepositoryList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from 'react';
import React, { useState, useRef, useEffect, useMemo } from 'react';
import { Bot, ChevronDown, Pause, Play } from 'lucide-react';
import { RepositoryCard } from './RepositoryCard';

Expand Down Expand Up @@ -34,6 +34,9 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
const [showDropdown, setShowDropdown] = useState(false);
const [isPaused, setIsPaused] = useState(false);
const [disableCardAnimations, setDisableCardAnimations] = useState(false);
const previousCategoryRef = useRef(selectedCategory);
const savedScrollYRef = useRef<number | null>(null);
const restoreScrollFrameRef = useRef<number | null>(null);

// 使用 useRef 来管理停止状态,确保在异步操作中能正确访问最新值
const shouldStopRef = useRef(false);
Expand Down Expand Up @@ -85,12 +88,52 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
const startIndex = filteredRepositories.length === 0 ? 0 : 1;
const endIndex = Math.min(visibleCount, filteredRepositories.length);
const visibleRepositories = filteredRepositories.slice(0, visibleCount);

// Reset visible count when filters or data change
const filterResetKey = useMemo(() => JSON.stringify({
selectedCategory,
query: searchFilters.query,
languages: searchFilters.languages,
tags: searchFilters.tags,
platforms: searchFilters.platforms,
sortBy: searchFilters.sortBy,
sortOrder: searchFilters.sortOrder,
minStars: searchFilters.minStars,
maxStars: searchFilters.maxStars,
isAnalyzed: searchFilters.isAnalyzed,
isSubscribed: searchFilters.isSubscribed,
}), [
selectedCategory,
searchFilters.query,
searchFilters.languages,
searchFilters.tags,
searchFilters.platforms,
searchFilters.sortBy,
searchFilters.sortOrder,
searchFilters.minStars,
searchFilters.maxStars,
searchFilters.isAnalyzed,
searchFilters.isSubscribed,
]);

// Reset visible count only when filter context changes.
useEffect(() => {
setVisibleCount(LOAD_BATCH);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedCategory, repositories, filteredRepositories.length]);
}, [filterResetKey]);

useEffect(() => {
if (previousCategoryRef.current !== selectedCategory) {
window.scrollTo({ top: 0, behavior: 'auto' });
previousCategoryRef.current = selectedCategory;
}
}, [selectedCategory]);

// Clamp visible count when result set becomes smaller, but do not collapse
// back to the initial batch during backend sync refreshes.
useEffect(() => {
setVisibleCount((count) => {
if (filteredRepositories.length === 0) return LOAD_BATCH;
return Math.min(count, filteredRepositories.length);
});
}, [filteredRepositories.length]);

// IntersectionObserver to load more on demand
useEffect(() => {
Expand All @@ -117,11 +160,39 @@ export const RepositoryList: React.FC<RepositoryListProps> = ({
useEffect(() => {
const handleSyncVisualState = (event: Event) => {
const customEvent = event as CustomEvent<{ isSyncing?: boolean }>;
setDisableCardAnimations(!!customEvent.detail?.isSyncing);
const isSyncing = !!customEvent.detail?.isSyncing;
setDisableCardAnimations(isSyncing);

if (isSyncing) {
savedScrollYRef.current = window.scrollY;
if (restoreScrollFrameRef.current !== null) {
cancelAnimationFrame(restoreScrollFrameRef.current);
restoreScrollFrameRef.current = null;
}
return;
}

const targetScrollY = savedScrollYRef.current;
if (targetScrollY === null) return;
if (window.scrollY === targetScrollY) {
savedScrollYRef.current = null;
return;
}

restoreScrollFrameRef.current = window.requestAnimationFrame(() => {
restoreScrollFrameRef.current = window.requestAnimationFrame(() => {
window.scrollTo({ top: targetScrollY, behavior: 'auto' });
restoreScrollFrameRef.current = null;
savedScrollYRef.current = null;
});
});
};

window.addEventListener('gsm:repository-sync-visual-state', handleSyncVisualState as EventListener);
return () => {
if (restoreScrollFrameRef.current !== null) {
cancelAnimationFrame(restoreScrollFrameRef.current);
}
window.removeEventListener('gsm:repository-sync-visual-state', handleSyncVisualState as EventListener);
};
}, []);
Expand Down
7 changes: 7 additions & 0 deletions src/store/useAppStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ interface AppActions {
// UI actions
setTheme: (theme: 'light' | 'dark') => void;
setCurrentView: (view: 'repositories' | 'releases' | 'settings') => void;
setSelectedCategory: (category: string) => void;
setLanguage: (language: 'zh' | 'en') => void;

// Update actions
Expand Down Expand Up @@ -112,6 +113,8 @@ type PersistedAppState = Partial<
| 'customCategories'
| 'assetFilters'
| 'theme'
| 'currentView'
| 'selectedCategory'
| 'language'
| 'searchFilters'
>
Expand Down Expand Up @@ -276,6 +279,7 @@ export const useAppStore = create<AppState & AppActions>()(
assetFilters: [],
theme: 'light',
currentView: 'repositories',
selectedCategory: 'all',
language: 'zh',
updateNotification: null,
analysisProgress: { current: 0, total: 0 },
Expand Down Expand Up @@ -429,6 +433,7 @@ export const useAppStore = create<AppState & AppActions>()(
// UI actions
setTheme: (theme) => set({ theme }),
setCurrentView: (currentView) => set({ currentView }),
setSelectedCategory: (selectedCategory) => set({ selectedCategory }),
setLanguage: (language) => set({ language }),

// Update actions
Expand Down Expand Up @@ -476,6 +481,8 @@ export const useAppStore = create<AppState & AppActions>()(

// 持久化UI设置
theme: state.theme,
currentView: state.currentView,
selectedCategory: state.selectedCategory,
language: state.language,

// backendApiSecret: 保留在内存中,不持久化(安全考虑)
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export interface AppState {
// UI
theme: 'light' | 'dark';
currentView: 'repositories' | 'releases' | 'settings';
selectedCategory: string;
language: 'zh' | 'en';

// Update
Expand Down
Loading