fix(plugins): allow reinstalling rolled-back marketplace versions

This commit is contained in:
Abeautifulsnow authored and GitHub committed 2026-09-17 21:07:13 +08:00
1 parent 9ded32c4c1
commit c040f9c9c3
11 files changed
+295 -15

No files matched your search

@@ -205,6 +205,10 @@ async function installMarketplaceListing(listing: MarketplacePluginListing) {
selectPlugin(result.plugin.manifest.id);
} catch (cause) {
toast(cause instanceof Error ? cause.message : String(cause), 8000);
// A failed install can still have mutated the store (a partially replaced version directory, for
// instance), so re-read the installed list instead of leaving the card on state it may no longer
// describe.
installedPlugins.value = await api.listPlugins().catch(() => installedPlugins.value);
} finally {
marketplaceInstallingKey.value = "";
}
@@ -870,6 +874,9 @@ onBeforeUnmount(() => {
<div class="mt-auto flex items-center justify-between gap-3 pt-4">
<div class="text-[11px] text-muted-foreground">
<span v-if="listing.status === 'unsupported'">{{ t("pluginPlatform.unsupportedTarget", { target: listing.target }) }}</span>
<!-- In the update state the left line states both versions: the badge above shows the
catalog latest version, which otherwise reads as the installed one. -->
<span v-else-if="listing.installed && listing.status === 'update'">{{ t("pluginPlatform.installedVersionUpdatable", { installed: listing.installed.manifest.version, latest: listing.plugin.latestVersion }) }}</span>
<span v-else-if="listing.installed">{{ t("pluginPlatform.installedVersion", { version: listing.installed.manifest.version }) }}</span>
<span v-else>{{ listing.plugin.license || t("pluginPlatform.licenseUnknown") }}</span>
</div>
+1
View File
@@ -184,6 +184,7 @@ export default {
permissionsCount: "{count} permissions",
unsupportedTarget: "Not available for {target}",
installedVersion: "Installed v{version}",
installedVersionUpdatable: "Installed v{installed} · Update to v{latest}",
licenseUnknown: "License not specified",
sourceRepository: "Source repository",
pluginHomepage: "Homepage",
+1
View File
@@ -187,6 +187,7 @@ export default withEnglishFallback({
permissionsCount: "{count} permisos",
unsupportedTarget: "No disponible para {target}",
installedVersion: "Instalado v{version}",
installedVersionUpdatable: "Instalado v{installed} · Actualizar a v{latest}",
licenseUnknown: "Licencia no especificada",
sourceRepository: "Repositorio de código fuente",
pluginHomepage: "Página principal",
+1
View File
@@ -186,6 +186,7 @@ export default withEnglishFallback({
permissionsCount: "{count} autorizzazioni",
unsupportedTarget: "Non disponibile per {target}",
installedVersion: "Installato v{version}",
installedVersionUpdatable: "Installato v{installed} · Aggiorna a v{latest}",
licenseUnknown: "Licenza non specificata",
sourceRepository: "Repository del codice sorgente",
pluginHomepage: "Sito web",
+1
View File
@@ -187,6 +187,7 @@ export default withEnglishFallback({
permissionsCount: "権限 {count} 件",
unsupportedTarget: "{target} では利用できません",
installedVersion: "インストール済み v{version}",
installedVersionUpdatable: "インストール済み v{installed} · v{latest} に更新可能",
licenseUnknown: "ライセンス未指定",
sourceRepository: "ソースリポジトリ",
pluginHomepage: "ホームページ",
+1
View File
@@ -186,6 +186,7 @@ export default withEnglishFallback({
permissionsCount: "권한 {count}개",
unsupportedTarget: "{target}에서는 사용할 수 없음",
installedVersion: "설치된 버전 v{version}",
installedVersionUpdatable: "설치된 버전 v{installed} · v{latest}(으)로 업데이트 가능",
licenseUnknown: "라이선스 미지정",
sourceRepository: "소스 저장소",
pluginHomepage: "홈페이지",
+1
View File
@@ -187,6 +187,7 @@ export default withEnglishFallback({
permissionsCount: "{count} permissões",
unsupportedTarget: "Indisponível para {target}",
installedVersion: "Instalado v{version}",
installedVersionUpdatable: "Instalado v{installed} · Atualizar para v{latest}",
licenseUnknown: "Licença não informada",
sourceRepository: "Repositório do código-fonte",
pluginHomepage: "Página inicial",
+1
View File
@@ -109,6 +109,7 @@ export default withEnglishFallback({
permissionsCount: "{count} 项权限",
unsupportedTarget: "暂不支持 {target}",
installedVersion: "已安装 v{version}",
installedVersionUpdatable: "已安装 v{installed} · 可更新至 v{latest}",
licenseUnknown: "未声明许可证",
sourceRepository: "源码仓库",
pluginHomepage: "主页",
+1
View File
@@ -187,6 +187,7 @@ export default withEnglishFallback({
permissionsCount: "{count} 項權限",
unsupportedTarget: "暫不支援 {target}",
installedVersion: "已安裝 v{version}",
installedVersionUpdatable: "已安裝 v{installed} · 可更新至 v{latest}",
licenseUnknown: "未指定授權條款",
sourceRepository: "原始碼倉庫",
pluginHomepage: "主頁",
+10
View File
@@ -65,6 +65,16 @@ describe("plugin marketplace listings", () => {
expect(buildMarketplacePluginListings([unsupported], [], "en")[0].status).toBe("unsupported");
});
it("keeps a newer installed version installed instead of offering a downgrade", () => {
const listings = buildMarketplacePluginListings([result], [installed("1.2.0")], "en");
// The catalog lags behind the installed build: the card must stay on the installed state.
expect(listings[0]).toMatchObject({ status: "installed", installed: { manifest: { version: "1.2.0" } } });
// Only "update" swaps the card's bottom-left line to the two-version text, so this state renders the
// plain installed line for 1.2.0 rather than the catalog's older 1.1.0.
expect(listings[0].plugin.latestVersion).toBe("1.1.0");
});
it("uses a universal artifact when the current target has no exact artifact", () => {
const universal = structuredClone(result);
universal.target = "linux-x64";