diff --git a/src/components/settings/SettingsPage.tsx b/src/components/settings/SettingsPage.tsx
index fe69acc9a..0e6604af1 100644
--- a/src/components/settings/SettingsPage.tsx
+++ b/src/components/settings/SettingsPage.tsx
@@ -380,7 +380,11 @@ export function SettingsPage({
-
+
diff --git a/src/components/settings/WebdavSyncSection.tsx b/src/components/settings/WebdavSyncSection.tsx
index c8ff65374..0161a9a73 100644
--- a/src/components/settings/WebdavSyncSection.tsx
+++ b/src/components/settings/WebdavSyncSection.tsx
@@ -33,6 +33,8 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { settingsApi } from "@/lib/api";
+import { ConfirmDialog } from "@/components/ConfirmDialog";
+import type { SettingsFormState } from "@/hooks/useSettings";
import type { RemoteSnapshotInfo, WebDavSyncSettings } from "@/types";
// ─── WebDAV service presets ─────────────────────────────────
@@ -110,6 +112,8 @@ type DialogType = "upload" | "download" | null;
interface WebdavSyncSectionProps {
config?: WebDavSyncSettings;
+ settings?: SettingsFormState;
+ onAutoSave?: (updates: Partial) => Promise;
}
// ─── ActionButton ───────────────────────────────────────────
@@ -151,7 +155,11 @@ function ActionButton({
// ─── Main component ─────────────────────────────────────────
-export function WebdavSyncSection({ config }: WebdavSyncSectionProps) {
+export function WebdavSyncSection({
+ config,
+ settings,
+ onAutoSave,
+}: WebdavSyncSectionProps) {
const { t } = useTranslation();
const queryClient = useQueryClient();
const [actionState, setActionState] = useState("idle");
@@ -180,6 +188,7 @@ export function WebdavSyncSection({ config }: WebdavSyncSectionProps) {
// Confirmation dialog state
const [dialogType, setDialogType] = useState(null);
const [remoteInfo, setRemoteInfo] = useState(null);
+ const [showAutoSyncConfirm, setShowAutoSyncConfirm] = useState(false);
const closeDialog = useCallback(() => {
setDialogType(null);
@@ -244,15 +253,34 @@ export function WebdavSyncSection({ config }: WebdavSyncSectionProps) {
}
}, [form.baseUrl, presetId]);
- const handleAutoSyncChange = useCallback((checked: boolean) => {
- setForm((prev) => ({ ...prev, autoSync: checked }));
+ const handleAutoSyncChange = useCallback(
+ (checked: boolean) => {
+ if (checked && !settings?.autoSyncConfirmed) {
+ setShowAutoSyncConfirm(true);
+ return;
+ }
+ setForm((prev) => ({ ...prev, autoSync: checked }));
+ setDirty(true);
+ setJustSaved(false);
+ if (justSavedTimerRef.current) {
+ clearTimeout(justSavedTimerRef.current);
+ justSavedTimerRef.current = null;
+ }
+ },
+ [settings?.autoSyncConfirmed],
+ );
+
+ const handleAutoSyncConfirm = useCallback(async () => {
+ setShowAutoSyncConfirm(false);
+ await onAutoSave?.({ autoSyncConfirmed: true });
+ setForm((prev) => ({ ...prev, autoSync: true }));
setDirty(true);
setJustSaved(false);
if (justSavedTimerRef.current) {
clearTimeout(justSavedTimerRef.current);
justSavedTimerRef.current = null;
}
- }, []);
+ }, [onAutoSave]);
const buildSettings = useCallback((): WebDavSyncSettings | null => {
const baseUrl = form.baseUrl.trim();
@@ -458,7 +486,9 @@ export function WebdavSyncSection({ config }: WebdavSyncSectionProps) {
const showAutoSyncError =
!!lastError && config?.status?.lastErrorSource === "auto";
const currentRemotePath = `/${form.remoteRoot.trim() || "cc-switch-sync"}/v2/db-v6/${form.profile.trim() || "default"}`;
- const remoteDbCompatDisplay = formatDbCompatVersion(remoteInfo?.dbCompatVersion);
+ const remoteDbCompatDisplay = formatDbCompatVersion(
+ remoteInfo?.dbCompatVersion,
+ );
const remoteIsLegacy = remoteInfo?.layout === "legacy";
// ─── Render ─────────────────────────────────────────────
@@ -866,6 +896,17 @@ export function WebdavSyncSection({ config }: WebdavSyncSectionProps) {
+
+ {/* ─── Auto-sync confirmation dialog ────────────────── */}
+ void handleAutoSyncConfirm()}
+ onCancel={() => setShowAutoSyncConfirm(false)}
+ />
);
}
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index a00875a14..90dc313c9 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -197,6 +197,11 @@
"title": "Model Health Check",
"message": "Health check tests provider connectivity by sending a direct API request. The following may cause check failures:\n\n• Official providers (uses OAuth login, no standalone API Key)\n• Some relay services (verify requests come from Claude Code CLI)\n• AWS Bedrock (uses IAM signature authentication)\n\nA failed check does not mean the provider is unusable — it only means it cannot be verified via a standalone request. Please refer to actual behavior within the application.",
"confirm": "I understand, proceed"
+ },
+ "autoSync": {
+ "title": "Enable Auto Sync",
+ "message": "When auto sync is enabled, every database change will be automatically uploaded to the WebDAV server.\n\nThis may result in significant network traffic. Please ensure your network and WebDAV service can handle frequent data transfers.",
+ "confirm": "I understand, enable"
}
},
"settings": {
diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json
index 02d9b166a..7eaf863e2 100644
--- a/src/i18n/locales/ja.json
+++ b/src/i18n/locales/ja.json
@@ -197,6 +197,11 @@
"title": "モデルヘルスチェック",
"message": "ヘルスチェックは API リクエストを直接送信してプロバイダーの接続性をテストします。以下の場合、チェックが失敗する可能性があります:\n\n• 公式プロバイダー(OAuth ログイン使用、独立した API キーなし)\n• 一部の中継サービス(リクエストが Claude Code CLI からのものか検証)\n• AWS Bedrock(IAM 署名認証を使用)\n\nチェック失敗はプロバイダーが使用不能であることを意味しません。独立したリクエストでの検証ができないことを示すだけです。アプリ内の実際の動作を基準にしてください。",
"confirm": "理解しました、続行する"
+ },
+ "autoSync": {
+ "title": "自動同期を有効にする",
+ "message": "自動同期を有効にすると、データベースの変更ごとに WebDAV サーバーへ自動アップロードされます。\n\nネットワークトラフィックが大幅に増加する可能性があります。ネットワーク環境と WebDAV サービスが頻繁なデータ転送に対応できることをご確認ください。",
+ "confirm": "理解しました、有効にする"
}
},
"settings": {
diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json
index 5551b2bae..2efdc196a 100644
--- a/src/i18n/locales/zh.json
+++ b/src/i18n/locales/zh.json
@@ -197,6 +197,11 @@
"title": "模型健康检测",
"message": "健康检测通过直接发送 API 请求来测试供应商连通性,以下情况可能导致检测失败:\n\n• 官方供应商(使用 OAuth 登录,无独立 API Key)\n• 部分中转服务(会校验请求是否来自 Claude Code CLI)\n• AWS Bedrock(使用 IAM 签名认证)\n\n检测失败不代表供应商不可用,仅表示无法通过独立请求验证。请以应用内的实际情况为准。",
"confirm": "我已了解,继续检测"
+ },
+ "autoSync": {
+ "title": "开启自动同步",
+ "message": "开启自动同步后,每次数据库变更都会自动上传到 WebDAV 服务器。\n\n这可能会产生较高的网络流量消耗,请确保您的网络环境和 WebDAV 服务支持频繁的数据传输。",
+ "confirm": "我已了解,继续开启"
}
},
"settings": {
diff --git a/src/types.ts b/src/types.ts
index b876aa57e..d40722abe 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -231,6 +231,8 @@ export interface Settings {
usageConfirmed?: boolean;
// User has confirmed the stream check first-run notice
streamCheckConfirmed?: boolean;
+ // User has confirmed the auto-sync traffic warning
+ autoSyncConfirmed?: boolean;
// 首选语言(可选,默认中文)
language?: "en" | "zh" | "ja";
@@ -500,11 +502,7 @@ export interface OpenClawWriteOutcome {
warnings: OpenClawHealthWarning[];
}
-export type OpenClawToolsProfile =
- | "minimal"
- | "coding"
- | "messaging"
- | "full";
+export type OpenClawToolsProfile = "minimal" | "coding" | "messaging" | "full";
// OpenClaw 供应商配置(settings_config 结构)
// 对应 OpenClaw 的 models.providers. 配置