From 9f7642e29cf36f86e6bb6187ec1b1ff494f2841a Mon Sep 17 00:00:00 2001 From: Jason Date: Sat, 4 Jul 2026 23:50:25 +0800 Subject: [PATCH] refactor(profiles): drop manual snapshot update now that switching autosaves Since switching projects automatically saves the current configuration back to the project being left, the per-project "update from current" button is redundant and even harmful: it allowed overwriting another project's snapshot with the current state, breaking the invariant that a project holds the state you last left it in. - Remove the resnapshot button and confirm dialog from the manage dialog; drop the now-unused scope prop and PROFILE_SCOPE_LABELS - Add a footer Close button to the manage dialog (overlay click is disabled app-wide, so it previously could only be closed via Esc) - Update manageDescription in all four locales to describe the autosave behavior; remove the two dead i18n keys - Fix a shadowed `warnings` vec in ProfileService::apply that silently dropped autosave-failure warnings before they reached the frontend - Mention auto-capture on next switch in the "no snapshot for this scope yet" warning and doc comments The backend update command keeps its resnapshot parameter; it is now only exercised by the pre-switch autosave path. --- src-tauri/src/commands/profile.rs | 7 +-- src-tauri/src/services/profile.rs | 8 +-- .../profiles/ProfileManageDialog.tsx | 63 ++++++------------- src/components/profiles/ProfileSwitcher.tsx | 1 - src/components/profiles/scope.ts | 7 --- src/i18n/locales/en.json | 4 +- src/i18n/locales/ja.json | 4 +- src/i18n/locales/zh-TW.json | 4 +- src/i18n/locales/zh.json | 4 +- 9 files changed, 29 insertions(+), 73 deletions(-) diff --git a/src-tauri/src/commands/profile.rs b/src-tauri/src/commands/profile.rs index 210fed053..41755c932 100644 --- a/src-tauri/src/commands/profile.rs +++ b/src-tauri/src/commands/profile.rs @@ -184,12 +184,7 @@ pub fn apply_profile( log::warn!("切换项目后停止代理服务失败: {e}"); } if let Some(app_state) = app_handle.try_state::() { - emit_profile_apply_events( - &app_handle, - app_state.inner(), - &profile_id, - scope, - ); + emit_profile_apply_events(&app_handle, app_state.inner(), &profile_id, scope); } }); } else { diff --git a/src-tauri/src/services/profile.rs b/src-tauri/src/services/profile.rs index cc08738e2..cd12423f2 100644 --- a/src-tauri/src/services/profile.rs +++ b/src-tauri/src/services/profile.rs @@ -264,7 +264,8 @@ impl ProfileService { } /// 更新项目:重命名(作用于共享实体)和/或以当前状态重拍快照 - /// (resnapshot 只覆盖 scope 分组的槽位,其余分组原样保留) + /// (resnapshot 只覆盖 scope 分组的槽位,其余分组原样保留; + /// 快照重拍仅由 [`Self::apply`] 切换前的自动保存触发,UI 不再暴露手动入口) pub fn update( state: &AppState, id: &str, @@ -314,7 +315,7 @@ impl ProfileService { /// /// 只作用于发起页所属分组内的应用,不碰其他分组的配置与 current 标记。 /// 该分组从未拍过快照时不改动任何配置,仅标记 current 并返回提示 - /// (用户可先绑定项目、再"以当前状态更新"补拍该侧快照)。 + /// (下次从该项目切走时,自动保存会补拍该侧快照)。 /// /// **切换前会自动保存旧项目**:若当前分组已绑定到另一个项目,先把当前 /// 状态写入那个旧项目(仅当前分组槽位),再加载目标项目。这样切走后 @@ -351,10 +352,9 @@ impl ProfileService { let payload: ProfilePayload = serde_json::from_str(&profile.payload) .map_err(|e| AppError::Config(format!("解析 profile payload 失败: {e}")))?; - let mut warnings = Vec::new(); if !payload.scope_captured(scope) { warnings.push(format!( - "no {} configuration captured in this project yet; marked as current without changes", + "no {} configuration captured in this project yet; marked as current without changes (it will be saved automatically when you switch away)", scope.as_str() )); } diff --git a/src/components/profiles/ProfileManageDialog.tsx b/src/components/profiles/ProfileManageDialog.tsx index e41307df4..027584554 100644 --- a/src/components/profiles/ProfileManageDialog.tsx +++ b/src/components/profiles/ProfileManageDialog.tsx @@ -1,10 +1,11 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; -import { Check, Pencil, RefreshCw, Trash2, X } from "lucide-react"; +import { Check, Pencil, Trash2, X } from "lucide-react"; import { Dialog, DialogContent, DialogDescription, + DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; @@ -16,17 +17,13 @@ import { useProfilesQuery, useUpdateProfileMutation, } from "@/lib/query/profiles"; -import type { ProfileScope } from "@/lib/api/profiles"; -import { PROFILE_SCOPE_LABELS } from "./scope"; interface ProfileManageDialogProps { isOpen: boolean; - scope: ProfileScope; onClose: () => void; } type PendingConfirm = { - type: "resnapshot" | "delete"; id: string; name: string; }; @@ -34,12 +31,11 @@ type PendingConfirm = { /** * 项目管理对话框(纯快照式) * - * 项目列表全应用共享,重命名/删除作用于共享实体; - * "以当前状态更新快照"只覆盖发起页所属分组(scope)的槽位。 + * 项目列表全应用共享,重命名/删除作用于共享实体。 + * 快照内容由切换时的自动保存维护,不提供手动重拍入口。 */ export function ProfileManageDialog({ isOpen, - scope, onClose, }: ProfileManageDialogProps) { const { t } = useTranslation(); @@ -71,11 +67,7 @@ export function ProfileManageDialog({ const handleConfirm = () => { if (!confirm) return; - if (confirm.type === "delete") { - deleteMutation.mutate(confirm.id); - } else { - updateMutation.mutate({ id: confirm.id, resnapshot: true, scope }); - } + deleteMutation.mutate(confirm.id); setConfirm(null); }; @@ -154,21 +146,6 @@ export function ProfileManageDialog({ > - + {confirm && ( setConfirm(null)} /> diff --git a/src/components/profiles/ProfileSwitcher.tsx b/src/components/profiles/ProfileSwitcher.tsx index 367070d2a..8273f76c3 100644 --- a/src/components/profiles/ProfileSwitcher.tsx +++ b/src/components/profiles/ProfileSwitcher.tsx @@ -246,7 +246,6 @@ export function ProfileSwitcher({ activeApp }: ProfileSwitcherProps) { setIsManageOpen(false)} /> diff --git a/src/components/profiles/scope.ts b/src/components/profiles/scope.ts index 7c8dab822..623983bba 100644 --- a/src/components/profiles/scope.ts +++ b/src/components/profiles/scope.ts @@ -12,13 +12,6 @@ export const APP_PROFILE_SCOPE: Partial> = { codex: "codex", }; -/** 分组显示名(产品名,不进 i18n;与后端托盘子菜单标签一致) */ -export const PROFILE_SCOPE_LABELS: Record = { - claude: "Claude Code", - "claude-desktop": "Claude Desktop", - codex: "Codex", -}; - /** 分组内的 payload 槽位 key(后端 ProfileScope::apps 的前端镜像) */ const SCOPE_SLOT_KEYS: Record)[]> = { claude: ["claude"], diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index f7492bf9f..73ba13faf 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1883,11 +1883,9 @@ }, "manage": "Manage projects…", "manageTitle": "Manage Projects", - "manageDescription": "Projects are configuration snapshots. To change a project, adjust your configuration first, then click \"Update from current\".", + "manageDescription": "Projects are configuration snapshots. When you switch projects, the current configuration is automatically saved back to the project you are leaving.", "namePlaceholder": "e.g. Development, Drawing", "rename": "Rename", - "updateSnapshot": "Update from current", - "updateSnapshotConfirm": "Overwrite the {{group}} side of project \"{{name}}\" with the current configuration? Other apps' snapshots are unaffected.", "noSnapshotForScope": "Not saved for this app yet", "delete": "Delete", "deleteConfirmTitle": "Confirm Delete", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 9f163b6d2..36a46cd80 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1883,11 +1883,9 @@ }, "manage": "プロジェクトを管理…", "manageTitle": "プロジェクト管理", - "manageDescription": "プロジェクトは設定のスナップショットです。内容を変更するには、先に設定を調整してから「現在の状態で更新」をクリックしてください。", + "manageDescription": "プロジェクトは設定のスナップショットです。プロジェクトを切り替えると、現在の設定は元のプロジェクトに自動的に保存されます。", "namePlaceholder": "例:開発、イラスト", "rename": "名前を変更", - "updateSnapshot": "現在の状態で更新", - "updateSnapshotConfirm": "プロジェクト「{{name}}」の {{group}} 側スナップショットを現在の設定で上書きしますか?他のアプリ側には影響しません。", "noSnapshotForScope": "このアプリ側は未保存", "delete": "削除", "deleteConfirmTitle": "削除の確認", diff --git a/src/i18n/locales/zh-TW.json b/src/i18n/locales/zh-TW.json index e098ba003..8ce60dbd6 100644 --- a/src/i18n/locales/zh-TW.json +++ b/src/i18n/locales/zh-TW.json @@ -1855,11 +1855,9 @@ }, "manage": "管理專案…", "manageTitle": "管理專案", - "manageDescription": "專案是設定快照。要修改專案內容,先手動調好設定,再點「以目前狀態更新」。", + "manageDescription": "專案是設定快照。切換專案時,目前設定會自動儲存回原專案。", "namePlaceholder": "例如:開發、繪圖", "rename": "重新命名", - "updateSnapshot": "以目前狀態更新", - "updateSnapshotConfirm": "用目前設定覆蓋專案 \"{{name}}\" 的 {{group}} 側快照?其他應用側的快照不受影響。", "noSnapshotForScope": "本側未儲存", "delete": "刪除", "deleteConfirmTitle": "確認刪除", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 1a5d87aaa..6c93a8b9e 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1883,11 +1883,9 @@ }, "manage": "管理项目…", "manageTitle": "管理项目", - "manageDescription": "项目是配置快照。要修改项目内容,先手动调好配置,再点\"以当前状态更新\"。", + "manageDescription": "项目是配置快照。切换项目时,当前配置会自动保存回原项目。", "namePlaceholder": "例如:开发、绘图", "rename": "重命名", - "updateSnapshot": "以当前状态更新", - "updateSnapshotConfirm": "用当前配置覆盖项目 \"{{name}}\" 的 {{group}} 侧快照?其他应用侧的快照不受影响。", "noSnapshotForScope": "本侧未保存", "delete": "删除", "deleteConfirmTitle": "确认删除",