From bd952893e0368e77d90ae71d2a9be24614c5f3db Mon Sep 17 00:00:00 2001 From: MatthewDaggitt Date: Thu, 14 May 2026 10:06:25 +0800 Subject: [PATCH] feat(component): display the number of requests per token left --- .../github/forms/GitHubTokenInput.tsx | 44 +++++++++++++++ .../github/analysis/RepoAnalysisForm.tsx | 6 ++ src/hooks/github/useTokenManagement.ts | 56 +++++++++++++++++++ src/services/github/api.ts | 43 +++++++++++++- 4 files changed, 148 insertions(+), 1 deletion(-) diff --git a/src/components/github/forms/GitHubTokenInput.tsx b/src/components/github/forms/GitHubTokenInput.tsx index 66b96fd..3296b17 100644 --- a/src/components/github/forms/GitHubTokenInput.tsx +++ b/src/components/github/forms/GitHubTokenInput.tsx @@ -16,6 +16,9 @@ interface GitHubTokenInputProps { onTokenDelete: () => void; hasSavedToken: boolean; hasPresetToken: boolean; + rateLimitRemaining: number | null; + rateLimitResetAt: number | null; + rateLimitLoading: boolean; } const GitHubTokenInput = ({ @@ -24,7 +27,38 @@ const GitHubTokenInput = ({ onTokenSave, onTokenDelete, hasSavedToken, + rateLimitRemaining, + rateLimitResetAt, + rateLimitLoading, }: GitHubTokenInputProps) => { + const getResetCountdownText = (): string => { + if (rateLimitResetAt === null) { + return "resets soon"; + } + + const nowInSeconds = Math.floor(Date.now() / 1000); + const remainingSeconds = Math.max(0, rateLimitResetAt - nowInSeconds); + const remainingMinutes = Math.ceil(remainingSeconds / 60); + + return `resets in ${remainingMinutes}m`; + }; + + const renderRateLimitCounter = (): string => { + if (!token.trim()) { + return "Hourly calls remaining: —"; + } + + if (rateLimitLoading) { + return "Hourly calls remaining: loading..."; + } + + if (rateLimitRemaining === null) { + return "Hourly calls remaining: unavailable"; + } + + return `Hourly calls remaining: ${rateLimitRemaining} • ${getResetCountdownText()}`; + }; + return ( @@ -63,6 +97,16 @@ const GitHubTokenInput = ({ + + {renderRateLimitCounter()} +