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:
YoVinchen
2025-11-12 10:47:34 +08:00
committed by GitHub
parent 32a2ba5ef6
commit 8a05e7bd3d
46 changed files with 2522 additions and 276 deletions

View File

@@ -30,7 +30,7 @@ fn read_override_from_store(app: &tauri::AppHandle) -> Option<PathBuf> {
let store = match app.store_builder("app_paths.json").build() {
Ok(store) => store,
Err(e) => {
log::warn!("无法创建 Store: {}", e);
log::warn!("无法创建 Store: {e}");
return None;
}
};
@@ -46,20 +46,18 @@ fn read_override_from_store(app: &tauri::AppHandle) -> Option<PathBuf> {
if !path.exists() {
log::warn!(
"Store 中配置的 app_config_dir 不存在: {:?}\n\
将使用默认路径。",
path
"Store 中配置的 app_config_dir 不存在: {path:?}\n\
将使用默认路径。"
);
return None;
}
log::info!("使用 Store 中的 app_config_dir: {:?}", path);
log::info!("使用 Store 中的 app_config_dir: {path:?}");
Some(path)
}
Some(_) => {
log::warn!(
"Store 中的 {} 类型不正确,应为字符串",
STORE_KEY_APP_CONFIG_DIR
"Store 中的 {STORE_KEY_APP_CONFIG_DIR} 类型不正确,应为字符串"
);
None
}
@@ -82,14 +80,14 @@ pub fn set_app_config_dir_to_store(
let store = app
.store_builder("app_paths.json")
.build()
.map_err(|e| AppError::Message(format!("创建 Store 失败: {}", e)))?;
.map_err(|e| AppError::Message(format!("创建 Store 失败: {e}")))?;
match path {
Some(p) => {
let trimmed = p.trim();
if !trimmed.is_empty() {
store.set(STORE_KEY_APP_CONFIG_DIR, Value::String(trimmed.to_string()));
log::info!("已将 app_config_dir 写入 Store: {}", trimmed);
log::info!("已将 app_config_dir 写入 Store: {trimmed}");
} else {
store.delete(STORE_KEY_APP_CONFIG_DIR);
log::info!("已从 Store 中删除 app_config_dir 配置");
@@ -103,7 +101,7 @@ pub fn set_app_config_dir_to_store(
store
.save()
.map_err(|e| AppError::Message(format!("保存 Store 失败: {}", e)))?;
.map_err(|e| AppError::Message(format!("保存 Store 失败: {e}")))?;
refresh_app_config_dir_override(app);
Ok(())