diff --git a/neode-ui/src/views/Marketplace.vue b/neode-ui/src/views/Marketplace.vue index 5225fa58..94918722 100644 --- a/neode-ui/src/views/Marketplace.vue +++ b/neode-ui/src/views/Marketplace.vue @@ -367,21 +367,38 @@ const installingApps = ref>(new Map()) const maxAttempts = ref(60) // Watch WebSocket data for real install progress from backend +// Also picks up installs started before this page was opened (Task 11: persist across nav) watch(() => store.packages, (packages) => { if (!packages) return for (const [appId, pkg] of Object.entries(packages)) { - const progress = pkg['install-progress'] - if (progress && pkg.state === 'installing' && installingApps.value.has(appId)) { - const current = installingApps.value.get(appId)! - const pct = progress.size > 0 ? Math.round((progress.downloaded / progress.size) * 100) : 0 - const downloadedMB = (progress.downloaded / (1024 * 1024)).toFixed(1) - const totalMB = (progress.size / (1024 * 1024)).toFixed(1) - installingApps.value.set(appId, { - ...current, - status: 'downloading', - progress: Math.min(pct, 95), - message: progress.size > 0 ? `Downloading: ${downloadedMB} / ${totalMB} MB (${pct}%)` : t('marketplace.downloading'), - }) + if ((pkg.state as string) === 'installing') { + const progress = pkg['install-progress'] + // If we don't have a local entry yet, create one from backend state + if (!installingApps.value.has(appId)) { + installingApps.value.set(appId, { + id: appId, + title: pkg.manifest?.title || appId, + status: 'downloading', + progress: 0, + message: t('common.installing'), + attempt: 0, + }) + } + if (progress) { + const current = installingApps.value.get(appId)! + const pct = progress.size > 0 ? Math.round((progress.downloaded / progress.size) * 100) : 0 + const downloadedMB = (progress.downloaded / (1024 * 1024)).toFixed(1) + const totalMB = (progress.size / (1024 * 1024)).toFixed(1) + installingApps.value.set(appId, { + ...current, + status: 'downloading', + progress: Math.min(pct, 95), + message: progress.size > 0 ? `Downloading: ${downloadedMB} / ${totalMB} MB (${pct}%)` : t('marketplace.downloading'), + }) + } + } else if (installingApps.value.has(appId) && (pkg.state as string) !== 'installing') { + // Install finished — remove from tracking + installingApps.value.delete(appId) } } }, { deep: true })