mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-26 23:56:02 +08:00
feat(tray): show coding-plan usage for Kimi / Zhipu / MiniMax
dc04165f surfaced tray usage badges for Claude/Codex/Gemini official
OAuth only. Chinese coding-plan providers already expose 5h + weekly
windows through coding_plan::get_coding_plan_quota, but two gaps kept
the tray from rendering them.
- format_script_summary read only data.first(), truncating the tier-
flattened UsageResult to a single window. Detect plan_name matching
TIER_FIVE_HOUR / TIER_WEEKLY_LIMIT and emit the "🟢 h12% w80%" layout
used by format_subscription_summary; worst utilization drives the
emoji. Copilot / balance / custom scripts keep the legacy single-
bucket output via fallback.
- usage_script previously required manual activation through
UsageScriptModal. Auto-inject meta.usage_script on Claude provider
creation when ANTHROPIC_BASE_URL matches a known coding plan, so the
tray lights up without the user opening the modal. Does not overwrite
existing usage_script on update.
Extract the URL route table out of UsageScriptModal into a shared
codingPlanProviders module so the modal, the creation hook, and the
Rust coding_plan::detect_provider mirror all agree on one list.
Add TIER_WEEKLY_LIMIT alongside TIER_FIVE_HOUR and a createUsageScript()
factory to collapse the duplicated default fields across four call
sites and drop the remaining stringly-typed tier names.
This commit is contained in:
@@ -3,7 +3,7 @@ import { Play, Wand2, Eye, EyeOff, Save } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { Provider, UsageScript, UsageData } from "@/types";
|
||||
import { Provider, UsageScript, UsageData, createUsageScript } from "@/types";
|
||||
import { usageApi, settingsApi, type AppId } from "@/lib/api";
|
||||
import { copilotGetUsage, copilotGetUsageForAccount } from "@/lib/api/copilot";
|
||||
import { useSettingsQuery } from "@/lib/query";
|
||||
@@ -21,6 +21,10 @@ import { FullScreenPanel } from "@/components/common/FullScreenPanel";
|
||||
import { ConfirmDialog } from "@/components/ConfirmDialog";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { TEMPLATE_TYPES, PROVIDER_TYPES } from "@/config/constants";
|
||||
import {
|
||||
CODING_PLAN_PROVIDERS,
|
||||
detectCodingPlanProvider,
|
||||
} from "@/config/codingPlanProviders";
|
||||
|
||||
interface UsageScriptModalProps {
|
||||
provider: Provider;
|
||||
@@ -114,21 +118,6 @@ const TEMPLATE_NAME_KEYS: Record<string, string> = {
|
||||
[TEMPLATE_TYPES.BALANCE]: "usageScript.templateBalance",
|
||||
};
|
||||
|
||||
/** Coding Plan 供应商选项 */
|
||||
const TOKEN_PLAN_PROVIDERS = [
|
||||
{ id: "kimi", label: "Kimi For Coding", pattern: /api\.kimi\.com\/coding/i },
|
||||
{
|
||||
id: "zhipu",
|
||||
label: "Zhipu GLM (智谱)",
|
||||
pattern: /bigmodel\.cn|api\.z\.ai/i,
|
||||
},
|
||||
{
|
||||
id: "minimax",
|
||||
label: "MiniMax",
|
||||
pattern: /api\.minimaxi?\.com|api\.minimax\.io/i,
|
||||
},
|
||||
] as const;
|
||||
|
||||
/** 官方余额查询供应商检测 */
|
||||
const BALANCE_PROVIDERS = [
|
||||
{ id: "deepseek", label: "DeepSeek", pattern: /api\.deepseek\.com/i },
|
||||
@@ -148,15 +137,6 @@ function detectBalanceProvider(baseUrl: string | undefined): boolean {
|
||||
return BALANCE_PROVIDERS.some((bp) => bp.pattern.test(baseUrl));
|
||||
}
|
||||
|
||||
/** 根据 Base URL 自动检测 Coding Plan 供应商 */
|
||||
function detectTokenPlanProvider(baseUrl: string | undefined): string | null {
|
||||
if (!baseUrl) return null;
|
||||
for (const cp of TOKEN_PLAN_PROVIDERS) {
|
||||
if (cp.pattern.test(baseUrl)) return cp.id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
||||
provider,
|
||||
appId,
|
||||
@@ -237,43 +217,24 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
||||
return {
|
||||
...savedScript,
|
||||
codingPlanProvider:
|
||||
detectTokenPlanProvider(providerCredentials.baseUrl) || "kimi",
|
||||
detectCodingPlanProvider(providerCredentials.baseUrl) || "kimi",
|
||||
};
|
||||
}
|
||||
return savedScript;
|
||||
}
|
||||
|
||||
// 新配置:如果 URL 匹配 Coding Plan,自动初始化
|
||||
const autoDetected = detectTokenPlanProvider(providerCredentials.baseUrl);
|
||||
const autoDetected = detectCodingPlanProvider(providerCredentials.baseUrl);
|
||||
if (autoDetected) {
|
||||
return {
|
||||
enabled: false,
|
||||
language: "javascript" as const,
|
||||
code: "",
|
||||
timeout: 10,
|
||||
autoQueryInterval: 5,
|
||||
codingPlanProvider: autoDetected,
|
||||
};
|
||||
return createUsageScript({ codingPlanProvider: autoDetected });
|
||||
}
|
||||
|
||||
// 新配置:如果 URL 匹配官方余额查询供应商,自动初始化
|
||||
if (detectBalanceProvider(providerCredentials.baseUrl)) {
|
||||
return {
|
||||
enabled: false,
|
||||
language: "javascript" as const,
|
||||
code: "",
|
||||
timeout: 10,
|
||||
autoQueryInterval: 5,
|
||||
};
|
||||
return createUsageScript();
|
||||
}
|
||||
|
||||
return {
|
||||
enabled: false,
|
||||
language: "javascript" as const,
|
||||
return createUsageScript({
|
||||
code: PRESET_TEMPLATES[TEMPLATE_TYPES.GENERAL],
|
||||
timeout: 10,
|
||||
autoQueryInterval: 5,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const [testing, setTesting] = useState(false);
|
||||
@@ -346,7 +307,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
||||
return TEMPLATE_TYPES.GENERAL;
|
||||
}
|
||||
// 新配置:如果 URL 匹配 Coding Plan 供应商,自动选择 Coding Plan 模板
|
||||
if (detectTokenPlanProvider(providerCredentials.baseUrl)) {
|
||||
if (detectCodingPlanProvider(providerCredentials.baseUrl)) {
|
||||
return TEMPLATE_TYPES.TOKEN_PLAN;
|
||||
}
|
||||
// 新配置:如果 URL 匹配官方余额查询供应商,自动选择 Balance 模板
|
||||
@@ -623,7 +584,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
||||
});
|
||||
} else if (presetName === TEMPLATE_TYPES.TOKEN_PLAN) {
|
||||
// Coding Plan 模板不需要脚本,使用 Rust 原生查询
|
||||
const autoDetected = detectTokenPlanProvider(
|
||||
const autoDetected = detectCodingPlanProvider(
|
||||
providerCredentials.baseUrl,
|
||||
);
|
||||
setScript({
|
||||
@@ -862,7 +823,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
||||
{t("usageScript.tokenPlanHint")}
|
||||
</p>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{TOKEN_PLAN_PROVIDERS.map((cp) => (
|
||||
{CODING_PLAN_PROVIDERS.map((cp) => (
|
||||
<Button
|
||||
key={cp.id}
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user