diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7354f888..dca059c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -436,7 +436,7 @@ jobs: - [Intel](https://github.com/op7418/CodePilot/releases/download/v${VERSION}/CodePilot-${VERSION}-x64.dmg) ### Windows - - [Windows 安装包](https://github.com/op7418/CodePilot/releases/download/v${VERSION}/CodePilot-Setup-${VERSION}.exe) + - [Windows 安装包](https://github.com/op7418/CodePilot/releases/download/v${VERSION}/CodePilot-Setup.${VERSION}.exe) ## 安装说明 diff --git a/package-lock.json b/package-lock.json index 5132b09f..a532dab3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2857,6 +2857,72 @@ "node": ">= 10.0.0" } }, + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", + "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/windows-sign/node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/windows-sign/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/windows-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/@emnapi/core": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", @@ -11867,6 +11933,15 @@ "buffer": "^5.1.0" } }, + "node_modules/cross-dirname": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", + "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -15010,7 +15085,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -21284,6 +21358,36 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/powershell-utils": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", diff --git a/src/app/api/settings/app/route.ts b/src/app/api/settings/app/route.ts index de1f68a2..200a8bfc 100644 --- a/src/app/api/settings/app/route.ts +++ b/src/app/api/settings/app/route.ts @@ -10,6 +10,7 @@ const ALLOWED_KEYS = [ 'anthropic_auth_token', 'anthropic_base_url', 'dangerously_skip_permissions', + 'disable_conflict_checking', 'generative_ui_enabled', 'locale', 'thinking_mode', diff --git a/src/components/layout/ConnectionStatus.tsx b/src/components/layout/ConnectionStatus.tsx index 373c61a9..72006c16 100644 --- a/src/components/layout/ConnectionStatus.tsx +++ b/src/components/layout/ConnectionStatus.tsx @@ -58,6 +58,16 @@ export function ConnectionStatus() { const [status, setStatus] = useState(null); const [dialogOpen, setDialogOpen] = useState(false); const [wizardOpen, setWizardOpen] = useState(false); + const [disableConflictChecking, setDisableConflictChecking] = useState(false); + + useEffect(() => { + fetch("/api/settings/app").then(async (res) => { + if (res.ok) { + const data = await res.json(); + setDisableConflictChecking((data.settings?.disable_conflict_checking) === "true"); + } + }).catch(() => { /* ignore */ }); + }, []); const isElectron = typeof window !== "undefined" && @@ -154,7 +164,7 @@ export function ConnectionStatus() { }, []); const connected = status?.connected ?? false; - const hasConflicts = (status?.otherInstalls?.length ?? 0) > 0; + const hasConflicts = (status?.otherInstalls?.length ?? 0) > 0 && !disableConflictChecking; const missingGit = status?.missingGit ?? false; const hasWarnings = hasConflicts || missingGit; diff --git a/src/components/layout/InstallWizard.tsx b/src/components/layout/InstallWizard.tsx index cbe75a3b..f2a1e8d0 100644 --- a/src/components/layout/InstallWizard.tsx +++ b/src/components/layout/InstallWizard.tsx @@ -116,9 +116,19 @@ export function InstallWizard({ const [logs, setLogs] = useState([]); const [copied, setCopied] = useState(false); const [prereqs, setPrereqs] = useState(null); + const [disableConflictChecking, setDisableConflictChecking] = useState(false); const logEndRef = useRef(null); const cleanupRef = useRef<(() => void) | null>(null); + useEffect(() => { + fetch("/api/settings/app").then(async (res) => { + if (res.ok) { + const data = await res.json(); + setDisableConflictChecking((data.settings?.disable_conflict_checking) === "true"); + } + }).catch(() => { /* ignore */ }); + }, []); + const scrollToBottom = useCallback(() => { logEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, []); @@ -267,7 +277,7 @@ export function InstallWizard({ }, [open, checkPrereqs]); const steps = progress?.steps ?? []; - const hasConflicts = (prereqs?.otherInstalls?.length ?? 0) > 0; + const hasConflicts = (prereqs?.otherInstalls?.length ?? 0) > 0 && !disableConflictChecking; return ( diff --git a/src/components/settings/GeneralSection.tsx b/src/components/settings/GeneralSection.tsx index c6791045..20256bdb 100644 --- a/src/components/settings/GeneralSection.tsx +++ b/src/components/settings/GeneralSection.tsx @@ -128,6 +128,8 @@ export function GeneralSection() { const [skipPermSaving, setSkipPermSaving] = useState(false); const [generativeUI, setGenerativeUI] = useState(true); const [generativeUISaving, setGenerativeUISaving] = useState(false); + const [disableConflictChecking, setDisableConflictChecking] = useState(false); + const [conflictCheckSaving, setConflictCheckSaving] = useState(false); const { accountInfo } = useAccountInfo(); const { t, locale, setLocale } = useTranslation(); @@ -140,6 +142,7 @@ export function GeneralSection() { setSkipPermissions(appSettings.dangerously_skip_permissions === "true"); // generative_ui_enabled defaults to true when not set setGenerativeUI(appSettings.generative_ui_enabled !== "false"); + setDisableConflictChecking(appSettings.disable_conflict_checking === "true"); } } catch { // ignore @@ -199,6 +202,26 @@ export function GeneralSection() { } }; + const handleDisableConflictCheckingToggle = async (checked: boolean) => { + setConflictCheckSaving(true); + try { + const res = await fetch("/api/settings/app", { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + settings: { disable_conflict_checking: checked ? "true" : "" }, + }), + }); + if (res.ok) { + setDisableConflictChecking(checked); + } + } catch { + // ignore + } finally { + setConflictCheckSaving(false); + } + }; + return (
@@ -236,6 +259,19 @@ export function GeneralSection() { /> + {/* Disable conflict checking toggle */} + + + + {/* Language picker */} (null); const [checking, setChecking] = useState(true); const [showCleanup, setShowCleanup] = useState(false); + const [disableConflictChecking, setDisableConflictChecking] = useState(false); + + useEffect(() => { + fetch('/api/settings/app').then(async (res) => { + if (res.ok) { + const data = await res.json(); + setDisableConflictChecking((data.settings?.disable_conflict_checking) === 'true'); + } + }).catch(() => { /* ignore */ }); + }, []); const checkStatus = useCallback(async () => { setChecking(true); @@ -120,7 +130,7 @@ export function ClaudeCodeCard({ status, onStatusChange }: ClaudeCodeCardProps) {claudeStatus.binaryPath && (

{claudeStatus.binaryPath}

)} - {(claudeStatus.otherInstalls?.length ?? 0) > 0 && ( + {(claudeStatus.otherInstalls?.length ?? 0) > 0 && !disableConflictChecking && (

{t('setup.claude.conflict')}

diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 7b331592..295ec6cb 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -98,6 +98,8 @@ const en = { 'settings.enableAutoApprove': 'Enable Auto-approve', 'settings.generativeUITitle': 'Generative UI', 'settings.generativeUIDesc': 'Enable interactive visualizations (charts, diagrams, mockups) in chat responses. Disabling saves tokens but removes visual generation capability.', + 'settings.disableConflictCheckingTitle': 'Hide Conflict Warnings', + 'settings.disableConflictCheckingDesc': 'Hide warnings about multiple Claude Code installations. Enable this if you intentionally run multiple versions side by side.', 'settings.language': 'Language', 'settings.languageDesc': 'Choose the display language for the interface', 'settings.usage': 'Usage', diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts index e7830681..b1062ff0 100644 --- a/src/i18n/zh.ts +++ b/src/i18n/zh.ts @@ -95,6 +95,8 @@ const zh: Record = { 'settings.enableAutoApprove': '启用自动批准', 'settings.generativeUITitle': '生成式 UI', 'settings.generativeUIDesc': '启用聊天中的交互式可视化功能(图表、流程图、原型图等)。关闭后可节省 token,但将无法生成可视化内容。', + 'settings.disableConflictCheckingTitle': '隐藏版本冲突警告', + 'settings.disableConflictCheckingDesc': '隐藏多个 Claude Code 安装版本的冲突警告。如果你有意同时使用多个版本,可以开启此选项。', 'settings.language': '语言', 'settings.languageDesc': '选择界面显示语言', 'settings.usage': '用量统计',