From aae2a756d6491bea728da0280ef79d8db22ca02d Mon Sep 17 00:00:00 2001 From: wsp Date: Thu, 29 Oct 2026 21:45:47 +0800 Subject: [PATCH] fix(installer): skip setup pages during updates Detect updates by matching a complete existing installation to the validated target path. Finish directly after payload installation, preserving existing model and theme configuration while launching the updated app when enabled. Document the separate update flow in installer guidance. --- OpenBitFun-Installer/AGENTS-CN.md | 2 ++ OpenBitFun-Installer/AGENTS.md | 3 ++ OpenBitFun-Installer/README.md | 5 +++ .../src/hooks/useInstaller.ts | 33 ++++++++++++++++++- 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/OpenBitFun-Installer/AGENTS-CN.md b/OpenBitFun-Installer/AGENTS-CN.md index e096c75d23..6be918588c 100644 --- a/OpenBitFun-Installer/AGENTS-CN.md +++ b/OpenBitFun-Installer/AGENTS-CN.md @@ -26,6 +26,8 @@ Language Select → Options → Progress → Model Setup → Theme Setup ``` +如果所选路径已有 OpenBitFun 安装,流程会在 `Progress` 后结束,保留已有的模型和主题配置。 + ## 命令 这些是命令参考,不是默认预检清单。PR 范围请以下方 Verification 为准。 diff --git a/OpenBitFun-Installer/AGENTS.md b/OpenBitFun-Installer/AGENTS.md index 336bcd60b2..10f1e5bff5 100644 --- a/OpenBitFun-Installer/AGENTS.md +++ b/OpenBitFun-Installer/AGENTS.md @@ -26,6 +26,9 @@ Install flow: Language Select → Options → Progress → Model Setup → Theme Setup ``` +For an existing installation at the selected path, the flow ends after +`Progress`; model and theme setup are retained from the existing installation. + ## Commands These are command references, not the default precheck list. Use Verification diff --git a/OpenBitFun-Installer/README.md b/OpenBitFun-Installer/README.md index bed1129cf5..e33607a651 100644 --- a/OpenBitFun-Installer/README.md +++ b/OpenBitFun-Installer/README.md @@ -114,6 +114,11 @@ Language Select → Options → Progress → Model Setup → Theme Setup language options install model config launch/close ``` +When the selected installation path already contains the current OpenBitFun +installation, the update flow ends after Progress. The existing model and theme +configuration are preserved, and the installer launches the updated application +when launch-after-install is enabled. + ## Development ### Prerequisites diff --git a/OpenBitFun-Installer/src/hooks/useInstaller.ts b/OpenBitFun-Installer/src/hooks/useInstaller.ts index aa52b6a45f..ab50d6b1db 100644 --- a/OpenBitFun-Installer/src/hooks/useInstaller.ts +++ b/OpenBitFun-Installer/src/hooks/useInstaller.ts @@ -52,6 +52,23 @@ export interface UseInstallerReturn { const STEPS: InstallStep[] = ['lang', 'options', 'progress', 'model', 'theme']; +function normalizeInstallPath(path: string): string { + return path.trim().replace(/[\\/]+$/, ''); +} + +function isSameInstallPath(left: string, right: string): boolean { + const normalizedLeft = normalizeInstallPath(left); + const normalizedRight = normalizeInstallPath(right); + if (normalizedLeft === normalizedRight) return true; + + // Windows registry paths are case-insensitive. Keep POSIX paths case-sensitive. + const isWindowsPath = /^[A-Za-z]:[\\/]/.test(normalizedLeft) + || /^[A-Za-z]:[\\/]/.test(normalizedRight) + || normalizedLeft.includes('\\') + || normalizedRight.includes('\\'); + return isWindowsPath && normalizedLeft.toLowerCase() === normalizedRight.toLowerCase(); +} + export function useInstaller(): UseInstallerReturn { const [previewOnly, setPreviewOnly] = useState(false); const [step, setStep] = useState('lang'); @@ -243,9 +260,16 @@ export function useInstaller(): UseInstallerReturn { setInstallationCompleted(false); setCanConfirmProgress(false); try { + const detectedInstall = await refreshExistingInstall(); const validated = await invoke('validate_install_path', { path: options.installPath, }); + const isUpdate = Boolean( + detectedInstall.detected + && detectedInstall.mainBinaryPresent + && detectedInstall.installLocation + && isSameInstallPath(detectedInstall.installLocation, validated.installPath), + ); const effectiveOptions = { ...options, installPath: validated.installPath, @@ -257,6 +281,13 @@ export function useInstaller(): UseInstallerReturn { setProgress({ step: 'prepare', percent: 0, message: '' }); await invoke('start_installation', { options: effectiveOptions }); setInstallationCompleted(true); + if (isUpdate) { + if (options.launchAfterInstall) { + await invoke('launch_application', { installPath: effectiveOptions.installPath }); + } + await invoke('close_installer'); + return; + } setStep('model'); try { const info = await readExistingInstall(); @@ -270,7 +301,7 @@ export function useInstaller(): UseInstallerReturn { } finally { setIsInstalling(false); } - }, [options, readExistingInstall]); + }, [options, readExistingInstall, refreshExistingInstall]); const confirmProgress = useCallback(() => { if (!canConfirmProgress) return;