From bfef4b0bdd6062b053d4994ed908797dea8c3de7 Mon Sep 17 00:00:00 2001 From: newbe36524 Date: Mon, 16 Feb 2026 15:32:55 +0800 Subject: [PATCH] 1 --- package-lock.json | 43 ++++++++----- package.json | 2 +- src/main/config-manager.ts | 49 +++++++++++++-- src/main/dependency-manager.ts | 63 ++++++++++++++++++- src/main/onboarding-manager.ts | 15 ++++- src/main/web-service-manager.ts | 16 ++++- .../onboarding/steps/DependencyInstaller.tsx | 5 +- src/renderer/store/thunks/dependencyThunks.ts | 21 +++---- 8 files changed, 172 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 331c5b68..26e43b77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -149,6 +149,7 @@ "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -785,6 +786,7 @@ "resolved": "https://registry.npmmirror.com/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", + "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -1381,7 +1383,6 @@ "dev": true, "license": "BSD-2-Clause", "optional": true, - "peer": true, "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", @@ -1403,7 +1404,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -1420,7 +1420,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "universalify": "^2.0.0" }, @@ -1435,7 +1434,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">= 10.0.0" } @@ -2505,6 +2503,7 @@ "resolved": "https://registry.npmmirror.com/@noble/ciphers/-/ciphers-1.3.0.tgz", "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", "license": "MIT", + "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -5408,6 +5407,7 @@ "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -5429,6 +5429,7 @@ "resolved": "https://registry.npmmirror.com/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -5439,6 +5440,7 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -5629,6 +5631,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6170,6 +6173,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7146,8 +7150,7 @@ "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "dev": true, "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/cross-env": { "version": "7.0.3", @@ -7671,6 +7674,7 @@ "integrity": "sha512-uOOBA3f+kW3o4KpSoMQ6SNpdXU7WtxlJRb9vCZgOvqhTz4b3GjcoWKstdisizNZLsylhTMv8TLHFPFW0Uxsj/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "app-builder-lib": "26.7.0", "builder-util": "26.4.1", @@ -8101,7 +8105,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", @@ -8122,7 +8125,6 @@ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -8136,7 +8138,8 @@ "version": "8.6.0", "resolved": "https://registry.npmmirror.com/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/embla-carousel-react": { "version": "8.6.0", @@ -9411,6 +9414,7 @@ "resolved": "https://registry.npmmirror.com/hono/-/hono-4.11.9.tgz", "integrity": "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=16.9.0" } @@ -9563,6 +9567,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.28.4" }, @@ -11488,7 +11493,6 @@ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "minimist": "^1.2.6" }, @@ -12253,7 +12257,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "commander": "^9.4.0" }, @@ -12271,7 +12274,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": "^12.20.0 || >=14" } @@ -12723,6 +12725,7 @@ "resolved": "https://registry.npmmirror.com/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -12753,6 +12756,7 @@ "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -12825,6 +12829,7 @@ "resolved": "https://registry.npmmirror.com/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", + "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -13091,7 +13096,8 @@ "version": "5.0.1", "resolved": "https://registry.npmmirror.com/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -13253,7 +13259,6 @@ "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -14384,7 +14389,8 @@ "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-4.1.18.tgz", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tailwindcss-animate": { "version": "1.0.7", @@ -14443,7 +14449,6 @@ "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" @@ -14698,7 +14703,8 @@ "version": "2.8.1", "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tw-animate-css": { "version": "1.4.0", @@ -14766,6 +14772,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15154,6 +15161,7 @@ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -15474,6 +15482,7 @@ "resolved": "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 5dca38cf..9325af82 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "dev:renderer": "vite", "dev:main": "tsc --watch", "dev:preload": "vite build --config vite.preload.config.ts --watch", - "dev": "rm -rf dist && concurrently -k \"npm run dev:renderer\" \"npm run dev:main\" \"npm run dev:preload\" \"wait-on dist/preload/index.mjs && wait-on dist/main/main.js && cross-env NODE_ENV=development HAGICO_CONFIG_PATH=local_data_root electron .\"", + "dev": "node -e \"const fs=require('fs');fs.existsSync('dist')&&fs.rmSync('dist',{recursive:true,force:true})\" && concurrently -k \"npm run dev:renderer\" \"npm run dev:main\" \"npm run dev:preload\" \"wait-on dist/preload/index.mjs && wait-on dist/main/main.js && cross-env NODE_ENV=development HAGICO_CONFIG_PATH=local_data_root electron .\"", "start": "npm run build:all && electron .", "prebuild": "npm run build:all", "build": "electron-builder", diff --git a/src/main/config-manager.ts b/src/main/config-manager.ts index b73df9c9..940b5240 100644 --- a/src/main/config-manager.ts +++ b/src/main/config-manager.ts @@ -53,8 +53,11 @@ export class ConfigManager { const dir = path.dirname(configPath); await fs.mkdir(dir, { recursive: true }); + // Sanitize config to handle Windows paths properly + const sanitizedConfig = this.sanitizeConfigForYaml(config); + // Convert to YAML with proper formatting - const content = yaml.dump(config, { + const content = yaml.dump(sanitizedConfig, { indent: 2, lineWidth: -1, sortKeys: false, @@ -70,6 +73,37 @@ export class ConfigManager { } } + /** + * Escape Windows path backslashes for YAML double-quoted strings + * Converts C:\Users\... to C:\\Users\... + */ + private escapePathForYaml(filePath: string): string { + return filePath.replace(/\\/g, '\\\\'); + } + + /** + * Sanitize config object to handle Windows paths properly + * Recursively processes strings that look like file paths + */ + private sanitizeConfigForYaml(config: any): any { + if (typeof config === 'string') { + // Check if this looks like a Windows path (contains drive letter and backslashes) + if (/^[A-Za-z]:\\/.test(config) || config.includes('\\')) { + return this.escapePathForYaml(config); + } + return config; + } else if (Array.isArray(config)) { + return config.map(item => this.sanitizeConfigForYaml(item)); + } else if (config !== null && typeof config === 'object') { + const sanitized: any = {}; + for (const [key, value] of Object.entries(config)) { + sanitized[key] = this.sanitizeConfigForYaml(value); + } + return sanitized; + } + return config; + } + /** * Update DataDir configuration in appsettings.yml * Config is stored in the installed version's config directory @@ -91,9 +125,12 @@ export class ConfigManager { config = {}; } - // Update DataDir with absolute path + // Update DataDir with absolute path (escape backslashes for YAML) config.DataDir = dataDir; + // Escape backslashes for YAML compatibility + const escapedDataDir = this.escapePathForYaml(dataDir); + // Check if file exists and has content let existingContent = ''; try { @@ -108,18 +145,18 @@ export class ConfigManager { if (existingContent.includes('DataDir:')) { // Replace existing DataDir const updatedContent = existingContent.replace( - /DataDir:\s*\S+/, - `DataDir: "${dataDir}"` + /DataDir:\s*"[^"]*"/, + `DataDir: "${escapedDataDir}"` ); await fs.writeFile(configPath, updatedContent, 'utf-8'); } else { // Append DataDir to the end of the file - const updatedContent = existingContent.trimEnd() + `\n\n# Data directory configuration\nDataDir: "${dataDir}"\n`; + const updatedContent = existingContent.trimEnd() + `\n\n# Data directory configuration\nDataDir: "${escapedDataDir}"\n`; await fs.writeFile(configPath, updatedContent, 'utf-8'); } } else { // File is empty or doesn't exist, create new config file with just DataDir - const content = `# Data directory configuration\nDataDir: "${dataDir}"\n`; + const content = `# Data directory configuration\nDataDir: "${escapedDataDir}"\n`; await fs.writeFile(configPath, content, 'utf-8'); } diff --git a/src/main/dependency-manager.ts b/src/main/dependency-manager.ts index a95a8782..a9d66c95 100644 --- a/src/main/dependency-manager.ts +++ b/src/main/dependency-manager.ts @@ -46,6 +46,10 @@ export class DependencyManager { private regionDetector: RegionDetector; private currentManifest: Manifest | null = null; private workingDirectory: string | null = null; + private checkPromise: Promise | null = null; // Cache ongoing check + private lastCheckTime: number = 0; + private readonly CHECK_CACHE_TTL = 2000; // Cache results for 2 seconds + private cachedResults: DependencyCheckResult[] | null = null; constructor(store?: Store>) { this.platform = process.platform; @@ -84,6 +88,16 @@ export class DependencyManager { return this.currentManifest; } + /** + * Clear cached check results + * Call this after installing dependencies or when forcing a refresh + */ + clearCheckCache(): void { + this.cachedResults = null; + this.lastCheckTime = 0; + log.info('[DependencyManager] Check cache cleared'); + } + /** * Read result.json file from script directory * Supports multiple result file names and formats for backward compatibility @@ -408,8 +422,40 @@ export class DependencyManager { /** * Check all dependencies from manifest + * Uses caching to avoid duplicate checks within a short time window */ async checkAllDependencies(): Promise { + const now = Date.now(); + + // Return cached results if still valid + if (this.cachedResults && (now - this.lastCheckTime) < this.CHECK_CACHE_TTL) { + log.info('[DependencyManager] Returning cached dependency check results'); + return this.cachedResults; + } + + // If a check is already in progress, wait for it + if (this.checkPromise) { + log.info('[DependencyManager] Waiting for ongoing dependency check'); + return this.checkPromise; + } + + // Start a new check + this.checkPromise = this.performCheck(); + + try { + const results = await this.checkPromise; + this.cachedResults = results; + this.lastCheckTime = now; + return results; + } finally { + this.checkPromise = null; + } + } + + /** + * Perform the actual dependency check + */ + private async performCheck(): Promise { // If manifest is available, use it if (this.currentManifest) { const { manifestReader } = await import('./manifest-reader.js'); @@ -456,6 +502,7 @@ export class DependencyManager { // Resolve check script path const { manifestReader } = await import('./manifest-reader.js'); const scriptPath = manifestReader.resolveScriptPath(entryPoint.check, this.workingDirectory); + const scriptDirectory = path.dirname(scriptPath); log.info('[DependencyManager] Executing check script:', scriptPath); @@ -465,7 +512,7 @@ export class DependencyManager { }); // Read check-result.json to get individual dependency status - const checkResultPath = path.join(this.workingDirectory, 'check-result.json'); + const checkResultPath = path.join(scriptDirectory, 'check-result.json'); let checkResultData: any = null; try { @@ -473,7 +520,7 @@ export class DependencyManager { checkResultData = JSON.parse(content); log.info('[DependencyManager] Read check-result.json with dependencies:', Object.keys(checkResultData.dependencies || {})); } catch (error) { - log.warn('[DependencyManager] Could not read check-result.json, using fallback'); + log.warn('[DependencyManager] Could not read check-result.json, using fallback', error); } // Map results for each dependency @@ -816,6 +863,7 @@ export class DependencyManager { try { // Resolve install script path const scriptPath = manifestReader.resolveScriptPath(entryPoint.install, this.workingDirectory); + const scriptDirectory = path.dirname(scriptPath); log.info('[DependencyManager] Executing batch install script:', scriptPath); @@ -834,7 +882,7 @@ export class DependencyManager { const parsedResult = this.parseResultSession(resultSession); // Read install-result.json to get individual dependency installation results - const installResultPath = path.join(this.workingDirectory, 'install-result.json'); + const installResultPath = path.join(scriptDirectory, 'install-result.json'); let installResultData: any = null; try { @@ -925,6 +973,9 @@ export class DependencyManager { } } + // Clear cache after installation completes so next check gets fresh results + this.clearCheckCache(); + return results; } @@ -968,10 +1019,16 @@ export class DependencyManager { installResult.installHint = dep.installHint; } + // Clear cache after installation completes so next check gets fresh results + this.clearCheckCache(); + return installResult; } catch (error) { log.error(`[DependencyManager] Failed to install ${dep.name}:`, error); + // Clear cache even on failure + this.clearCheckCache(); + // Return failed result with install hint return { success: false, diff --git a/src/main/onboarding-manager.ts b/src/main/onboarding-manager.ts index f6a9c757..a3b0667b 100644 --- a/src/main/onboarding-manager.ts +++ b/src/main/onboarding-manager.ts @@ -500,6 +500,8 @@ export class OnboardingManager { /** * Start the web service + * Note: This method now includes dependency status check to ensure consistency + * with the homepage startup behavior */ async startWebService( versionId: string, @@ -523,6 +525,17 @@ export class OnboardingManager { return { success: false, error: 'Version not found' }; } + // Check version status - ensure dependencies are satisfied (same as homepage) + if (version.status !== 'installed-ready') { + const missingDeps = version.dependencies?.filter(dep => !dep.installed || dep.versionMismatch) || []; + log.warn('[OnboardingManager] Version not ready:', version.status, 'missing deps:', missingDeps.length); + + return { + success: false, + error: `Dependencies not satisfied. ${missingDeps.length} dependencies are missing or have version mismatches. Please install dependencies first.` + }; + } + // Send initial progress if (onProgress) { onProgress({ @@ -545,7 +558,7 @@ export class OnboardingManager { this.webServiceManager.setEntryPoint(null); } - // Start the service + // Start the service using the standard startup logic (same as homepage) const startResult = await this.webServiceManager.start(); if (startResult.success) { diff --git a/src/main/web-service-manager.ts b/src/main/web-service-manager.ts index eb54507f..7373604d 100644 --- a/src/main/web-service-manager.ts +++ b/src/main/web-service-manager.ts @@ -232,7 +232,12 @@ export class PCodeWebServiceManager { // Execute script return new Promise((resolve, reject) => { - const child = spawn(scriptPath, [], { + // On Windows with shell: true, paths with spaces need to be quoted + const command = (process.platform === 'win32' && scriptPath.includes(' ')) + ? `"${scriptPath}"` + : scriptPath; + + const child = spawn(command, [], { cwd: workingDirectory, shell: true, stdio: 'ignore', @@ -359,9 +364,10 @@ export class PCodeWebServiceManager { }); } - // Windows: detach to run independently + // Windows: use shell for .bat files, detach to run independently // Unix: keep attached for lifecycle management if (process.platform === 'win32') { + options.shell = true; // Required for executing .bat files options.detached = true; options.windowsHide = true; } else { @@ -379,6 +385,12 @@ export class PCodeWebServiceManager { private getSpawnCommand(): { command: string; args: string[] } { const scriptPath = this.getStartupScriptPath(); const args = this.getPlatformSpecificArgs(); + + // On Windows with shell: true, paths with spaces need to be quoted + if (process.platform === 'win32' && scriptPath.includes(' ')) { + return { command: `"${scriptPath}"`, args }; + } + return { command: scriptPath, args }; } diff --git a/src/renderer/components/onboarding/steps/DependencyInstaller.tsx b/src/renderer/components/onboarding/steps/DependencyInstaller.tsx index 6d3505f1..90eaab50 100644 --- a/src/renderer/components/onboarding/steps/DependencyInstaller.tsx +++ b/src/renderer/components/onboarding/steps/DependencyInstaller.tsx @@ -110,7 +110,10 @@ function DependencyInstaller() { const handleInstallAll = () => { if (installProgress.installing || !downloadProgress?.version) return; console.log('[DependencyInstaller] Installing all dependencies for version:', downloadProgress.version); - dispatch(installFromManifest(downloadProgress.version)); + dispatch(installFromManifest({ + versionId: downloadProgress.version, + context: 'onboarding', + })); }; // Get all dependencies (not just missing ones) for detailed display diff --git a/src/renderer/store/thunks/dependencyThunks.ts b/src/renderer/store/thunks/dependencyThunks.ts index 4e8153ff..0f8f123a 100644 --- a/src/renderer/store/thunks/dependencyThunks.ts +++ b/src/renderer/store/thunks/dependencyThunks.ts @@ -109,21 +109,20 @@ export const checkDependenciesAfterInstall = createAsyncThunk( } // Step 2: Execute actual dependency check (this takes time) - // Get missing dependencies to populate the dependency list - console.log('[checkDependenciesAfterInstall] Getting missing dependencies...'); - const missingDeps: DependencyItem[] = await window.electronAPI.getMissingDependencies(versionId); - console.log('[checkDependenciesAfterInstall] Got missing deps:', missingDeps.length, 'items'); + // Get ALL dependencies (including installed ones) in a single check + console.log('[checkDependenciesAfterInstall] Getting all dependencies...'); + const allDeps: DependencyItem[] = await window.electronAPI.getAllDependencies(versionId); + console.log('[checkDependenciesAfterInstall] Got all deps:', allDeps.length, 'items'); - // Store dependencies in state for the UI to display + // Filter missing dependencies from all dependencies + const missingDeps = allDeps.filter(dep => !dep.installed || dep.versionMismatch); + console.log('[checkDependenciesAfterInstall] Missing deps:', missingDeps.length, 'items'); + + // Store missing dependencies in state for the UI to display dispatch(fetchDependenciesSuccess(missingDeps)); - // For onboarding context, also get ALL dependencies and store in onboardingSlice + // For onboarding context, store ALL dependencies in onboardingSlice if (context === 'onboarding') { - // Get ALL dependencies (including installed ones) for detailed display - console.log('[checkDependenciesAfterInstall] Getting all dependencies...'); - const allDeps: DependencyItem[] = await window.electronAPI.getAllDependencies(versionId); - console.log('[checkDependenciesAfterInstall] Got all deps:', allDeps.length, 'items'); - // Store the results in onboarding state for detailed display dispatch(setDependencyCheckResults(allDeps.map(dep => ({ key: dep.key,