Feature/support multi env#2512
Conversation
…ions Introduce profile-based config directory system where each profile can have its own config dirs and current provider IDs per tool. Adds: - ConfigDirProfile struct with 18 fields (id, name, 6 config dirs, 6 current providers) - Profile management functions (get, upsert, delete, set_active) - 4 new Tauri commands exposed to frontend - Unit tests for struct serialization and default fields Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When an active profile exists, read/write current_provider_* fields from/to the profile instead of top-level AppSettings fields. Falls back to top-level fields for backward compatibility when no profile is active. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ration Config directory getter functions now read from active profile first, falling back to top-level legacy fields. Adds automatic migration that creates a "默认" profile from existing top-level fields on first load. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add TypeScript ConfigDirProfile type with 14 fields (config dirs + current provider IDs per tool). Add 4 API methods for profile CRUD: getConfigDirProfiles, upsertConfigDirProfile, deleteConfigDirProfile, setActiveConfigDirProfile. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add profile state management to useDirectorySettings hook with createProfile, updateProfile, deleteProfile, and switchProfile functions. Add profile selector UI (dropdown + new/delete buttons) to DirectorySettings component. Wire profile props through SettingsPage. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Profile CRUD operations now update settings state to ensure changes are persisted when settings are saved: - createProfile: sets activeConfigDirProfileId and configDirProfiles - updateProfile: syncs configDirProfiles to settings - deleteProfile: syncs configDirProfiles and clears activeProfileId - switchProfile: syncs activeConfigDirProfileId to settings - updateDirectoryState: when active profile exists, writes to profile fields instead of top-level settings fields Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add ProfileSwitcher component for quick environment profile switching on the home page. Button shows icon + current profile name, with popover menu listing all profiles with radio-style selection indicator. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Claude finished @farion1231's task in 2m 4s —— View job Code Review: Feature/support multi env
SummaryThis PR adds multi-environment profile support — users can define named Issues🔴 Functional / Correctness1. Stale closure in When updating a profile directory, setProfiles((prev) =>
prev.map((p) => (p.id === activeProfileId ? updatedProfile : p)),
);
// ⚠️ 'profiles' here is still the old value — stale closure
onUpdateSettings({
configDirProfiles: profiles.map((p) =>
p.id === activeProfileId ? updatedProfile : p,
),
});This should derive the new list once and use it for both: const newProfiles = profiles.map((p) => (p.id === activeProfileId ? updatedProfile : p));
setProfiles(newProfiles);
onUpdateSettings({ configDirProfiles: newProfiles });The same stale-closure pattern appears in 2. Directory inputs show top-level values, not active-profile values In onUpdateProfile={updateProfile}But in 3. TOCTOU in pub fn set_active_config_dir_profile(id: &str) -> Result<(), AppError> {
let settings = get_settings(); // read lock released here
if !settings.config_dir_profiles.iter().any(|p| p.id == id) {
return Err(...);
}
mutate_settings(|settings| { ... }) // write lock acquired here
}Between the read and the write, a concurrent call could delete the profile. On a single-user desktop app this is unlikely, but the check could be moved inside pub fn set_active_config_dir_profile(id: &str) -> Result<(), AppError> {
let id = id.to_string();
mutate_settings(|settings| {
if settings.config_dir_profiles.iter().any(|p| p.id == id) {
settings.active_config_dir_profile_id = Some(id.clone());
}
})
}🟡 UX / Design4. const name = prompt("Profile 名称:");
if (name) onCreateProfile?.(name);Using the browser's native 5. interface DirectorySettingsProps {
...
onUpdateProfile?: (profile: ConfigDirProfile) => Promise<void>; // declared ✓
}
export function DirectorySettings({
...,
onDeleteProfile, // destructured
onSwitchProfile, // destructured
// onUpdateProfile is NOT destructured → silently dropped
}: DirectorySettingsProps) {This means there is no way for the user to rename an existing profile from the settings UI. 6. Missing delete confirmation The delete button in 7. Hardcoded Chinese in migration default profile name ( name: "默认".to_string(),This profile name is baked into 8. const dirPath = (newActive as unknown as Record<string, string | undefined>)[meta.key];
const key = meta.key as keyof ConfigDirProfile;
const dirPath = newActive[key] as string | undefined;🟢 Minor / Polish9. onChange={(e) => onSwitchProfile?.(e.target.value)}The promise is not awaited and errors are silently swallowed. Consider adding error handling or disabling the select during the async operation. 10. Double Two separate onUpdateSettings({
configDirProfiles: newProfiles,
...(activeProfileId === id ? { activeConfigDirProfileId: undefined } : {}),
});11. Missing i18n key for "环境配置集" ( The section header 12. Unused "profileSwitcher": {
"title": "Environment Profile",
"manage": "Manage Profiles", // ← no component uses this key
...
}What's Well Done
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c254ddda56
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@claude review (Re-running with the updated review prompt for comparison.) |
|
Claude finished @farion1231's task in 2m 25s —— View job Code Review (re-run with updated prompt)Three Important findings, plus a couple of nits. No verdict — leaving the call to you. Important1. Directory inputs in Settings show top-level values, not the active profile's values confidence ~90
Fix: derive the displayed values from the active profile when one exists, falling back to top-level fields, e.g. compute 2.
if (activeProfileId === id) {
setActiveProfileId(undefined);
onUpdateSettings({ activeConfigDirProfileId: undefined });
}
3. App-level
Cheapest fix: hoist profile state into a react-query key (e.g. Nits
|
…tion sync Add a new Tauri command to retrieve the currently active config dir profile. Fix the profile deletion logic to fetch the actual active profile from backend instead of setting it to undefined, ensuring proper sync when a profile is deleted. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Fix directory inputs showing top-level values instead of active profile's values - Add getActiveProfileDirectoryValues getter to derive display values from active profile - Replace native prompt() with Dialog component for profile creation - Add i18n support for profile dialogs (zh/en/ja) - Implement onUpdateProfile usage for rename functionality - Add rename button and dialog for profile renaming Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ture - Fix stale closure bug in updateDirectoryState and createProfile - Fix TOCTOU race condition in set_active_config_dir_profile - Replace unsafe type cast with proper keyof access in switchProfile - Add delete confirmation dialog for profile deletion - Change hardcoded Chinese "默认" to "Default" in migration - Properly handle async in profile select onChange Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add icons (Pencil, Plus, Trash2, AlertTriangle) to profile action buttons - Improve button styling with inline-flex layout and proper spacing - Style delete button with destructive colors for better UX - Enhance delete confirmation dialog with warning styling - Add i18n translations for delete dialog title and confirmation message Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary / 概述
支持多环境
Related Issue / 关联 Issue
如何同时管理windows环境与WSL环境的切换
Screenshots / 截图
Checklist / 检查清单
pnpm typecheckpasses / 通过 TypeScript 类型检查pnpm format:checkpasses / 通过代码格式检查cargo clippypasses (if Rust code changed) / 通过 Clippy 检查(如修改了 Rust 代码)