From a8d291e581b5fa97a5270f2c0dc1b06084577012 Mon Sep 17 00:00:00 2001 From: RockChinQ <45992437+RockChinQ@users.noreply.github.com> Date: Mon, 21 Sep 2026 17:04:57 +0000 Subject: [PATCH] fix(plugins): retain Host progress fallback after master merge --- .../PluginInstallTaskContext.tsx | 2 + .../plugin-install-task/install-progress.ts | 9 +++ web/tests/unit/install-progress.test.mjs | 57 +++++++++++++++++++ .../knowledge-engine-marketplace.test.mjs | 5 +- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/web/src/app/home/plugins/components/plugin-install-task/PluginInstallTaskContext.tsx b/web/src/app/home/plugins/components/plugin-install-task/PluginInstallTaskContext.tsx index 8fecd0eee..34a7e44db 100644 --- a/web/src/app/home/plugins/components/plugin-install-task/PluginInstallTaskContext.tsx +++ b/web/src/app/home/plugins/components/plugin-install-task/PluginInstallTaskContext.tsx @@ -159,6 +159,7 @@ export function asyncTaskToPluginInstallTask( stage, downloadCurrent: num(md.download_current), downloadTotal: num(md.download_total), + reportedProgress: num(md.progress_percent), stageElapsedSeconds: 0, }), ); @@ -326,6 +327,7 @@ export function PluginInstallTaskProvider({ stage, downloadCurrent, downloadTotal, + reportedProgress: num(md.progress_percent), stageElapsedSeconds: (Date.now() - stageStartedAt) / 1000, }); const progress = Math.min( diff --git a/web/src/app/home/plugins/components/plugin-install-task/install-progress.ts b/web/src/app/home/plugins/components/plugin-install-task/install-progress.ts index d84751c5e..519e0b4ce 100644 --- a/web/src/app/home/plugins/components/plugin-install-task/install-progress.ts +++ b/web/src/app/home/plugins/components/plugin-install-task/install-progress.ts @@ -106,6 +106,8 @@ export interface StageProgressInput { stage: InstallStage; downloadCurrent?: number; downloadTotal?: number; + /** Host coarse stage progress, used only when measured bytes are absent. */ + reportedProgress?: number; /** Seconds spent in the current stage, used to bound fallback drift. */ stageElapsedSeconds: number; } @@ -136,6 +138,13 @@ export function computeStageProgress(input: StageProgressInput): number { return clampToRange(Math.round(start + (end - start) * ratio), start, end); } + if ( + input.reportedProgress != null && + Number.isFinite(input.reportedProgress) + ) { + return clampToRange(input.reportedProgress, start, end); + } + // Nothing measurable to show yet: drift slowly, but never past this stage's // own ceiling (hence `end - start - 1`, leaving the final point to the real // stage transition). diff --git a/web/tests/unit/install-progress.test.mjs b/web/tests/unit/install-progress.test.mjs index 96fae40c3..84f176ed6 100644 --- a/web/tests/unit/install-progress.test.mjs +++ b/web/tests/unit/install-progress.test.mjs @@ -116,6 +116,63 @@ test('fallback drift never spills into the next stage range', () => { } }); +test('preserves Host stage progress when byte counts are unavailable', () => { + assert.equal( + computeStageProgress({ + stage: InstallStage.DOWNLOADING, + reportedProgress: 23, + stageElapsedSeconds: 0, + }), + 23, + ); + assert.equal( + computeStageProgress({ + stage: InstallStage.INSTALLING_DEPS, + reportedProgress: 64, + stageElapsedSeconds: 0, + }), + 64, + ); + assert.equal( + mapActionToStage('checking plugin update'), + InstallStage.CHECKING, + ); + assert.equal( + mapActionToStage('validating plugin package'), + InstallStage.VALIDATING, + ); + assert.equal( + mapActionToStage('applying plugin update'), + InstallStage.INSTALLING_DEPS, + ); + assert.equal( + mapActionToStage('refreshing plugin components'), + InstallStage.LAUNCHING, + ); + assert.equal(mapActionToStage('plugin updated'), InstallStage.DONE); +}); + +test('measured bytes override Host coarse progress and fallback stays bounded', () => { + assert.equal( + computeStageProgress({ + stage: InstallStage.DOWNLOADING, + downloadCurrent: 90, + downloadTotal: 100, + reportedProgress: 15, + stageElapsedSeconds: 40, + }), + 41, + ); + for (const reportedProgress of [-5, 100]) { + const progress = computeStageProgress({ + stage: InstallStage.DOWNLOADING, + reportedProgress, + stageElapsedSeconds: 0, + }); + assert.ok(progress >= 5 && progress <= 45); + } +}); + test('a missing or zero download total falls back to bounded drift', () => { const [start, end] = STAGE_PROGRESS_RANGE[InstallStage.DOWNLOADING]; diff --git a/web/tests/unit/knowledge-engine-marketplace.test.mjs b/web/tests/unit/knowledge-engine-marketplace.test.mjs index b01bc947a..a803ba3c3 100644 --- a/web/tests/unit/knowledge-engine-marketplace.test.mjs +++ b/web/tests/unit/knowledge-engine-marketplace.test.mjs @@ -71,10 +71,11 @@ test('offers KnowledgeEngine marketplace plugins inside the selector', () => { test('tracks plugin upgrades as recoverable multistep async tasks', () => { assert.match(taskContextSource, /name\.startsWith\('plugin-upgrade-'\)/); assert.match(taskContextSource, /operation: PluginTaskOperation/); - assert.match(taskContextSource, /progress_percent/); + assert.match(taskContextSource, /computeStageProgress/); + assert.match(taskContextSource, /INSTALL_PROGRESS_CAP/); assert.match(progressDialogSource, /InstallStage\.CHECKING/); assert.match(progressDialogSource, /InstallStage\.VALIDATING/); - assert.match(progressDialogSource, /InstallStage\.ACTIVATING/); + assert.match(progressDialogSource, /InstallStage\.LAUNCHING/); assert.match(progressDialogSource, /plugins\.installProgress\.updateTitle/); for (const source of [installedPluginsSource, homeSidebarSource]) { assert.match(