+
+ @if (_editingProfile != null)
+ {
+ @T("envConfig.editingProfile", ("name", _editingProfile.ProfileName))
+ }
+ else if (_isAddingProfile)
+ {
+ @T("envConfig.addProfile")
+ }
+ else
+ {
+ @T("envConfig.editingDefault")
+ }
+
+ @if (_editingProfile != null || _isAddingProfile)
+ {
+
+ }
+
+
+
+ @if (_isAddingProfile || _editingProfile != null)
+ {
+
-
+ @if (_editingProfile == null && !_isAddingProfile)
+ {
+
+ }
+ else
+ {
+
+ }
}
+ else if (_editingProfile != null || _isAddingProfile)
+ {
+
@T("envConfig.saveProfile")
+ }
else
{
@T("envConfig.save")
@@ -158,6 +297,13 @@
private CliToolConfig? _selectedTool;
private List
_envVars = new();
+ // 配置方案相关
+ private List _profiles = new();
+ private CliToolEnvProfile? _editingProfile = null;
+ private bool _isAddingProfile = false;
+ private string _newProfileName = string.Empty;
+ private CliToolEnvProfile? _profilePendingDelete = null;
+
// 本地化相关
private Dictionary _translations = new();
private string _currentLanguage = "zh-CN";
@@ -173,19 +319,42 @@
await LoadTranslationsAsync();
}
+ private async Task ReloadEditorForCurrentStateAsync()
+ {
+ if (_selectedTool == null)
+ {
+ _envVars = new List();
+ return;
+ }
+
+ if (_profiles.Any(p => p.IsActive))
+ {
+ _envVars = new List();
+ return;
+ }
+
+ var envVars = await CliToolEnvironmentService.GetEnvironmentVariablesAsync(_selectedTool.Id);
+ _envVars = envVars.Select(kvp => new EnvVarItem { Key = kvp.Key, Value = kvp.Value }).ToList();
+ }
+
public async Task ShowAsync(CliToolConfig tool)
{
_selectedTool = tool;
_isVisible = true;
_isLoading = true;
+ _editingProfile = null;
+ _isAddingProfile = false;
+ _newProfileName = string.Empty;
StateHasChanged();
try
{
_currentLanguage = await L.GetCurrentLanguageAsync();
await LoadTranslationsAsync();
- var envVars = await CliToolEnvironmentService.GetEnvironmentVariablesAsync(tool.Id);
- _envVars = envVars.Select(kvp => new EnvVarItem { Key = kvp.Key, Value = kvp.Value }).ToList();
+
+ // 加载配置方案列表
+ _profiles = await CliToolEnvironmentService.GetProfilesAsync(tool.Id);
+ await ReloadEditorForCurrentStateAsync();
}
catch (Exception ex)
{
@@ -203,6 +372,11 @@
_isVisible = false;
_selectedTool = null;
_envVars.Clear();
+ _profiles.Clear();
+ _editingProfile = null;
+ _isAddingProfile = false;
+ _newProfileName = string.Empty;
+ _profilePendingDelete = null;
StateHasChanged();
}
@@ -235,6 +409,151 @@
}
}
+ // ── 配置方案操作 ──
+
+ private void StartAddProfile()
+ {
+ _isAddingProfile = true;
+ _editingProfile = null;
+ _newProfileName = string.Empty;
+ _profilePendingDelete = null;
+ _envVars = new List();
+ StateHasChanged();
+ }
+
+ private void EditProfile(CliToolEnvProfile profile)
+ {
+ _editingProfile = profile;
+ _isAddingProfile = false;
+ _newProfileName = profile.ProfileName;
+ _profilePendingDelete = null;
+
+ try
+ {
+ if (!string.IsNullOrWhiteSpace(profile.EnvVarsJson))
+ {
+ var dict = System.Text.Json.JsonSerializer.Deserialize>(profile.EnvVarsJson) ?? new();
+ _envVars = dict.Select(kvp => new EnvVarItem { Key = kvp.Key, Value = kvp.Value }).ToList();
+ }
+ else
+ {
+ _envVars = new List();
+ }
+ }
+ catch
+ {
+ _envVars = new List();
+ }
+ StateHasChanged();
+ }
+
+ private async Task CancelProfileEdit()
+ {
+ _editingProfile = null;
+ _isAddingProfile = false;
+ _newProfileName = string.Empty;
+ _profilePendingDelete = null;
+ await ReloadEditorForCurrentStateAsync();
+ StateHasChanged();
+ }
+
+ private async Task ActivateProfile(CliToolEnvProfile profile)
+ {
+ if (_selectedTool == null) return;
+ _isLoading = true;
+ StateHasChanged();
+ try
+ {
+ await CliToolEnvironmentService.ActivateProfileAsync(_selectedTool.Id, profile.Id);
+ _profiles = await CliToolEnvironmentService.GetProfilesAsync(_selectedTool.Id);
+ _editingProfile = null;
+ _isAddingProfile = false;
+ _newProfileName = string.Empty;
+ _profilePendingDelete = null;
+ await ReloadEditorForCurrentStateAsync();
+ Console.WriteLine($"方案已激活: {profile.ProfileName}");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"激活方案失败: {ex.Message}");
+ }
+ finally
+ {
+ _isLoading = false;
+ StateHasChanged();
+ }
+ }
+
+ private async Task DeactivateProfiles()
+ {
+ if (_selectedTool == null) return;
+ _isLoading = true;
+ StateHasChanged();
+ try
+ {
+ await CliToolEnvironmentService.DeactivateProfilesAsync(_selectedTool.Id);
+ _profiles = await CliToolEnvironmentService.GetProfilesAsync(_selectedTool.Id);
+ _editingProfile = null;
+ _isAddingProfile = false;
+ _newProfileName = string.Empty;
+ _profilePendingDelete = null;
+ await ReloadEditorForCurrentStateAsync();
+ Console.WriteLine("已取消所有方案激活");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"取消激活失败: {ex.Message}");
+ }
+ finally
+ {
+ _isLoading = false;
+ StateHasChanged();
+ }
+ }
+
+ private void RequestDeleteProfile(CliToolEnvProfile profile)
+ {
+ _profilePendingDelete = profile;
+ StateHasChanged();
+ }
+
+ private void CancelDeleteProfile()
+ {
+ _profilePendingDelete = null;
+ StateHasChanged();
+ }
+
+ private async Task DeleteProfile(CliToolEnvProfile profile)
+ {
+ if (_selectedTool == null) return;
+ _profilePendingDelete = null;
+ _isLoading = true;
+ StateHasChanged();
+ try
+ {
+ await CliToolEnvironmentService.DeleteProfileAsync(_selectedTool.Id, profile.Id);
+ _profiles = await CliToolEnvironmentService.GetProfilesAsync(_selectedTool.Id);
+ if (_editingProfile?.Id == profile.Id)
+ {
+ await CancelProfileEdit();
+ }
+ else
+ {
+ await ReloadEditorForCurrentStateAsync();
+ }
+ Console.WriteLine($"方案已删除: {profile.ProfileName}");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"删除方案失败: {ex.Message}");
+ }
+ finally
+ {
+ _isLoading = false;
+ StateHasChanged();
+ }
+ }
+
private async Task Save()
{
if (_selectedTool == null) return;
@@ -244,26 +563,48 @@
try
{
- // 过滤掉空的键或空的值的环境变量,只保存有实际值的配置
+ // 过滤掉空键或空值的条目
var validEnvVars = _envVars
.Where(ev => !string.IsNullOrWhiteSpace(ev.Key) && !string.IsNullOrWhiteSpace(ev.Value))
.ToDictionary(ev => ev.Key.Trim(), ev => ev.Value!.Trim());
- var success = await CliToolEnvironmentService.SaveEnvironmentVariablesAsync(_selectedTool.Id, validEnvVars);
-
- if (success)
+ if (_isAddingProfile || _editingProfile != null)
{
- Console.WriteLine("环境变量保存成功");
- Close();
+ // 保存配置方案(新建和编辑均使用 _newProfileName,支持重命名)
+ var profileName = _newProfileName.Trim();
+ if (string.IsNullOrWhiteSpace(profileName)) profileName = T("envConfig.unnamedProfile");
+
+ var profileId = _editingProfile?.Id ?? 0;
+ var savedProfile = await CliToolEnvironmentService.SaveProfileAsync(_selectedTool.Id, profileId, profileName, validEnvVars);
+ if (savedProfile != null)
+ {
+ _profiles = await CliToolEnvironmentService.GetProfilesAsync(_selectedTool.Id);
+ await CancelProfileEdit();
+ Console.WriteLine($"方案已保存: {profileName}");
+ }
+ else
+ {
+ Console.WriteLine("方案保存失败");
+ }
}
else
{
- Console.WriteLine("环境变量保存失败");
+ // 保存默认配置
+ var success = await CliToolEnvironmentService.SaveEnvironmentVariablesAsync(_selectedTool.Id, validEnvVars);
+ if (success)
+ {
+ Console.WriteLine("默认环境变量保存成功");
+ Close();
+ }
+ else
+ {
+ Console.WriteLine("环境变量保存失败");
+ }
}
}
catch (Exception ex)
{
- Console.WriteLine($"保存环境变量失败: {ex.Message}");
+ Console.WriteLine($"保存失败: {ex.Message}");
}
finally
{
diff --git a/WebCodeCli/Resources/Localization/en-US.json b/WebCodeCli/Resources/Localization/en-US.json
index 342338f..164e592 100644
--- a/WebCodeCli/Resources/Localization/en-US.json
+++ b/WebCodeCli/Resources/Localization/en-US.json
@@ -142,9 +142,9 @@
"event": "Event ({type})"
},
"content": {
- "threadId": "Thread ID: {id}",
- "threadCreated": "A new conversation thread has been created.",
- "turnStarted": "Waiting for response...",
+ "threadId": "Thread ID: {id}",
+ "threadCreated": "A new conversation thread has been created.",
+ "turnStarted": "Waiting for response...",
"turnCompleted": "This turn has completed.",
"turnCompletedWithUsage": "This turn has completed. See token usage below."
},
@@ -343,7 +343,26 @@
"addButton": "Add Environment Variable",
"resetButton": "Reset to Default",
"saving": "Saving...",
- "save": "Save"
+ "save": "Save",
+ "profilesTitle": "Configuration Profiles",
+ "profilesDesc": "Create multiple environment variable profiles to quickly switch between AI configurations",
+ "addProfile": "New Profile",
+ "profileNamePlaceholder": "Profile name, e.g. OpenAI / DeepSeek / Anthropic",
+ "activateProfile": "Activate",
+ "activeProfile": "Active",
+ "editProfile": "Edit",
+ "deleteProfile": "Delete",
+ "deleteProfileConfirm": "Delete this configuration profile?",
+ "deactivateProfile": "Deactivate",
+ "profileSaved": "Profile saved",
+ "profileActivated": "Profile activated",
+ "profileDeactivated": "Switched back to default config",
+ "profileDeleted": "Profile deleted",
+ "editingProfile": "Editing profile: {name}",
+ "editingDefault": "Editing default config",
+ "saveProfile": "Save Profile",
+ "unnamedProfile": "Unnamed profile",
+ "cancelEdit": "Cancel Edit"
},
"contextPreview": {
"title": "Context Preview",
@@ -800,4 +819,3 @@
}
}
}
-
diff --git a/WebCodeCli/Resources/Localization/ja-JP.json b/WebCodeCli/Resources/Localization/ja-JP.json
index 3e914f7..690bbff 100644
--- a/WebCodeCli/Resources/Localization/ja-JP.json
+++ b/WebCodeCli/Resources/Localization/ja-JP.json
@@ -191,9 +191,9 @@
"event": "イベント({type})"
},
"content": {
- "threadId": "スレッドID: {id}",
- "threadCreated": "新しい会話スレッドを作成しました。",
- "turnStarted": "応答待ち...",
+ "threadId": "スレッドID: {id}",
+ "threadCreated": "新しい会話スレッドを作成しました。",
+ "turnStarted": "応答待ち...",
"turnCompleted": "このターンは完了しました。",
"turnCompletedWithUsage": "このターンは完了しました。下のトークン統計をご確認ください。"
},
@@ -390,7 +390,26 @@
"addButton": "環境変数を追加",
"resetButton": "既定に戻す",
"saving": "保存中...",
- "save": "保存"
+ "save": "保存",
+ "profilesTitle": "設定プロファイル",
+ "profilesDesc": "複数の環境変数プロファイルを作成して、AI設定をすばやく切り替えられます",
+ "addProfile": "新規プロファイル",
+ "profileNamePlaceholder": "プロファイル名(例: OpenAI / DeepSeek / Anthropic)",
+ "activateProfile": "有効化",
+ "activeProfile": "有効中",
+ "editProfile": "編集",
+ "deleteProfile": "削除",
+ "deleteProfileConfirm": "このプロファイルを削除しますか?",
+ "deactivateProfile": "無効化",
+ "profileSaved": "プロファイルを保存しました",
+ "profileActivated": "プロファイルを有効化しました",
+ "profileDeactivated": "デフォルト設定に戻しました",
+ "profileDeleted": "プロファイルを削除しました",
+ "editingProfile": "プロファイルを編集中:{name}",
+ "editingDefault": "デフォルト設定を編集",
+ "saveProfile": "プロファイルを保存",
+ "unnamedProfile": "名称未設定のプロファイル",
+ "cancelEdit": "編集をキャンセル"
},
"fileSearch": {
"debugLabel": "デバッグ",
@@ -798,4 +817,3 @@
}
}
}
-
diff --git a/WebCodeCli/Resources/Localization/ko-KR.json b/WebCodeCli/Resources/Localization/ko-KR.json
index 2e3b069..0c76d44 100644
--- a/WebCodeCli/Resources/Localization/ko-KR.json
+++ b/WebCodeCli/Resources/Localization/ko-KR.json
@@ -191,9 +191,9 @@
"event": "이벤트 ({type})"
},
"content": {
- "threadId": "스레드 ID: {id}",
- "threadCreated": "새 대화 스레드를 만들었습니다.",
- "turnStarted": "응답을 기다리는 중...",
+ "threadId": "스레드 ID: {id}",
+ "threadCreated": "새 대화 스레드를 만들었습니다.",
+ "turnStarted": "응답을 기다리는 중...",
"turnCompleted": "이번 턴이 완료되었습니다.",
"turnCompletedWithUsage": "이번 턴이 완료되었습니다. 아래 토큰 통계를 확인하세요."
},
@@ -390,7 +390,26 @@
"addButton": "환경 변수 추가",
"resetButton": "기본값으로 재설정",
"saving": "저장 중...",
- "save": "저장"
+ "save": "저장",
+ "profilesTitle": "구성 프로파일",
+ "profilesDesc": "여러 환경 변수 프로파일을 생성하여 AI 구성을 빠르게 전환할 수 있습니다",
+ "addProfile": "새 프로파일",
+ "profileNamePlaceholder": "프로파일 이름 (예: OpenAI / DeepSeek / Anthropic)",
+ "activateProfile": "활성화",
+ "activeProfile": "활성",
+ "editProfile": "편집",
+ "deleteProfile": "삭제",
+ "deleteProfileConfirm": "이 구성 프로파일을 삭제하시겠습니까?",
+ "deactivateProfile": "비활성화",
+ "profileSaved": "프로파일이 저장되었습니다",
+ "profileActivated": "프로파일이 활성화되었습니다",
+ "profileDeactivated": "기본 구성으로 전환되었습니다",
+ "profileDeleted": "프로파일이 삭제되었습니다",
+ "editingProfile": "프로파일 편집 중: {name}",
+ "editingDefault": "기본 구성 편집",
+ "saveProfile": "프로파일 저장",
+ "unnamedProfile": "이름 없는 프로파일",
+ "cancelEdit": "편집 취소"
},
"fileSearch": {
"debugLabel": "디버그",
@@ -798,4 +817,3 @@
}
}
}
-
diff --git a/WebCodeCli/Resources/Localization/zh-CN.json b/WebCodeCli/Resources/Localization/zh-CN.json
index 273c951..7c69044 100644
--- a/WebCodeCli/Resources/Localization/zh-CN.json
+++ b/WebCodeCli/Resources/Localization/zh-CN.json
@@ -142,9 +142,9 @@
"event": "事件 ({type})"
},
"content": {
- "threadId": "线程标识: {id}",
- "threadCreated": "已创建新的对话线程",
- "turnStarted": "等待响应...",
+ "threadId": "线程标识: {id}",
+ "threadCreated": "已创建新的对话线程",
+ "turnStarted": "等待响应...",
"turnCompleted": "本轮交互已完成。",
"turnCompletedWithUsage": "本轮交互已完成,详见下方 token 统计。"
},
@@ -343,7 +343,26 @@
"addButton": "添加环境变量",
"resetButton": "重置为默认",
"saving": "保存中...",
- "save": "保存"
+ "save": "保存",
+ "profilesTitle": "配置方案",
+ "profilesDesc": "创建多套环境变量方案,一键切换不同的 AI 配置",
+ "addProfile": "新建方案",
+ "profileNamePlaceholder": "方案名称,如 OpenAI / DeepSeek / Anthropic",
+ "activateProfile": "激活",
+ "activeProfile": "当前激活",
+ "editProfile": "编辑",
+ "deleteProfile": "删除",
+ "deleteProfileConfirm": "确认删除此配置方案?",
+ "deactivateProfile": "取消激活",
+ "profileSaved": "方案已保存",
+ "profileActivated": "方案已激活",
+ "profileDeactivated": "已切换回默认配置",
+ "profileDeleted": "方案已删除",
+ "editingProfile": "正在编辑方案:{name}",
+ "editingDefault": "编辑默认配置",
+ "saveProfile": "保存方案",
+ "unnamedProfile": "未命名方案",
+ "cancelEdit": "取消编辑"
},
"contextPreview": {
"title": "上下文预览",
@@ -471,7 +490,9 @@
"workspaceRootDesc": "用于存放会话工作文件的目录。Docker 部署时建议使用默认值 {path}",
"configTipTitle": "配置说明",
"claudeConfigDesc": "配置 Claude Code 所需的环境变量。如果暂时不使用,可以跳过此步骤,稍后在设置中配置。",
- "codexConfigDesc": "配置 Codex (OpenAI) 所需的环境变量。如果暂时不使用,可以跳过此步骤,稍后在设置中配置。", "codexDocsLink": "查看 Codex 配置文档", "opencodeConfigDesc": "配置 OpenCode CLI 所需的环境变量。如果暂时不使用,可以跳过此步骤,稍后在设置中配置。",
+ "codexConfigDesc": "配置 Codex (OpenAI) 所需的环境变量。如果暂时不使用,可以跳过此步骤,稍后在设置中配置。",
+ "codexDocsLink": "查看 Codex 配置文档",
+ "opencodeConfigDesc": "配置 OpenCode CLI 所需的环境变量。如果暂时不使用,可以跳过此步骤,稍后在设置中配置。",
"opencodeDocsLink": "查看 OpenCode 环境变量文档",
"envVarNameLabel": "环境变量名",
"envVarValueLabel": "值",
@@ -798,4 +819,3 @@
}
}
}
-