feat(gemini): add Gemini provider integration (#202)
* feat(gemini): add Gemini provider integration - Add gemini_config.rs module for .env file parsing - Extend AppType enum to support Gemini - Implement GeminiConfigEditor and GeminiFormFields components - Add GeminiIcon with standardized 1024x1024 viewBox - Add Gemini provider presets configuration - Update i18n translations for Gemini support - Extend ProviderService and McpService for Gemini * fix(gemini): resolve TypeScript errors, add i18n support, and fix MCP logic **Critical Fixes:** - Fix TS2741 errors in tests/msw/state.ts by adding missing Gemini type definitions - Fix ProviderCard.extractApiUrl to support GOOGLE_GEMINI_BASE_URL display - Add missing apps.gemini i18n keys (zh/en) for proper app name display - Fix MCP service Gemini cross-app duplication logic to prevent self-copy **Technical Details:** - tests/msw/state.ts: Add gemini default providers, current ID, and MCP config - ProviderCard.tsx: Check both ANTHROPIC_BASE_URL and GOOGLE_GEMINI_BASE_URL - services/mcp.rs: Skip Gemini in sync_other_side logic with unreachable!() guards - Run pnpm format to auto-fix code style issues **Verification:** - ✅ pnpm typecheck passes - ✅ pnpm format completed * feat(gemini): enhance authentication and config parsing - Add strict and lenient .env parsing modes - Implement PackyCode partner authentication detection - Support Google OAuth official authentication - Auto-configure security.auth.selectedType for PackyCode - Add comprehensive test coverage for all auth types - Update i18n for OAuth hints and Gemini config --------- Co-authored-by: Jason <farion1231@gmail.com>
This commit is contained in:
@@ -267,7 +267,8 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
||||
|
||||
// 判断是否应该显示凭证配置区域
|
||||
const shouldShowCredentialsConfig =
|
||||
selectedTemplate === TEMPLATE_KEYS.GENERAL || selectedTemplate === TEMPLATE_KEYS.NEW_API;
|
||||
selectedTemplate === TEMPLATE_KEYS.GENERAL ||
|
||||
selectedTemplate === TEMPLATE_KEYS.NEW_API;
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
||||
@@ -334,9 +335,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
||||
{selectedTemplate === TEMPLATE_KEYS.GENERAL && (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="usage-api-key">
|
||||
API Key
|
||||
</Label>
|
||||
<Label htmlFor="usage-api-key">API Key</Label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id="usage-api-key"
|
||||
@@ -353,18 +352,24 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
||||
type="button"
|
||||
onClick={() => setShowApiKey(!showApiKey)}
|
||||
className="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors"
|
||||
aria-label={showApiKey ? t("apiKeyInput.hide") : t("apiKeyInput.show")}
|
||||
aria-label={
|
||||
showApiKey
|
||||
? t("apiKeyInput.hide")
|
||||
: t("apiKeyInput.show")
|
||||
}
|
||||
>
|
||||
{showApiKey ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||
{showApiKey ? (
|
||||
<EyeOff size={16} />
|
||||
) : (
|
||||
<Eye size={16} />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="usage-base-url">
|
||||
Base URL
|
||||
</Label>
|
||||
<Label htmlFor="usage-base-url">Base URL</Label>
|
||||
<Input
|
||||
id="usage-base-url"
|
||||
type="text"
|
||||
@@ -383,9 +388,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
||||
{selectedTemplate === TEMPLATE_KEYS.NEW_API && (
|
||||
<>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="usage-newapi-base-url">
|
||||
Base URL
|
||||
</Label>
|
||||
<Label htmlFor="usage-newapi-base-url">Base URL</Label>
|
||||
<Input
|
||||
id="usage-newapi-base-url"
|
||||
type="text"
|
||||
@@ -408,19 +411,34 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
||||
type={showAccessToken ? "text" : "password"}
|
||||
value={script.accessToken || ""}
|
||||
onChange={(e) =>
|
||||
setScript({ ...script, accessToken: e.target.value })
|
||||
setScript({
|
||||
...script,
|
||||
accessToken: e.target.value,
|
||||
})
|
||||
}
|
||||
placeholder={t("usageScript.accessTokenPlaceholder")}
|
||||
placeholder={t(
|
||||
"usageScript.accessTokenPlaceholder",
|
||||
)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
{script.accessToken && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowAccessToken(!showAccessToken)}
|
||||
onClick={() =>
|
||||
setShowAccessToken(!showAccessToken)
|
||||
}
|
||||
className="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors"
|
||||
aria-label={showAccessToken ? t("apiKeyInput.hide") : t("apiKeyInput.show")}
|
||||
aria-label={
|
||||
showAccessToken
|
||||
? t("apiKeyInput.hide")
|
||||
: t("apiKeyInput.show")
|
||||
}
|
||||
>
|
||||
{showAccessToken ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||
{showAccessToken ? (
|
||||
<EyeOff size={16} />
|
||||
) : (
|
||||
<Eye size={16} />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -448,9 +466,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
|
||||
|
||||
{/* 脚本编辑器 */}
|
||||
<div>
|
||||
<Label className="mb-2">
|
||||
{t("usageScript.queryScript")}
|
||||
</Label>
|
||||
<Label className="mb-2">{t("usageScript.queryScript")}</Label>
|
||||
<JsonEditor
|
||||
value={script.code}
|
||||
onChange={(code) => setScript({ ...script, code })}
|
||||
|
||||
Reference in New Issue
Block a user