From 423d35033c435d5d69f0b6b7475bad07e64f71c4 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:05:39 +0800 Subject: [PATCH 01/19] allow viewing changelog for other versions Co-authored-by: mimo-v2.5 --- .../hmcl/ui/instances/AddonUpdatesPage.java | 63 ++++++++++++++----- .../resources/assets/lang/I18N.properties | 1 + .../resources/assets/lang/I18N_de.properties | 1 + .../resources/assets/lang/I18N_lzh.properties | 1 + .../resources/assets/lang/I18N_uk.properties | 1 + .../resources/assets/lang/I18N_zh.properties | 1 + .../assets/lang/I18N_zh_CN.properties | 1 + .../jackhuang/hmcl/addon/LocalAddonFile.java | 3 +- .../hmcl/addon/mod/LocalModFile.java | 2 +- .../resourcepack/ResourcePackZipFile.java | 2 +- 10 files changed, 59 insertions(+), 17 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index b48622eb98..796dfa557a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -19,6 +19,7 @@ import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXCheckBox; +import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXDialogLayout; import javafx.beans.property.*; import javafx.beans.value.ObservableValue; @@ -27,10 +28,8 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.*; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; +import javafx.scene.layout.*; +import javafx.util.StringConverter; import org.jackhuang.hmcl.addon.LocalAddonFile; import org.jackhuang.hmcl.addon.LocalAddonManager; import org.jackhuang.hmcl.addon.RemoteAddon; @@ -54,7 +53,9 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; @@ -300,23 +301,55 @@ public void setSource(String source) { } private static final class AddonChangelog extends JFXDialogLayout { + private final Map changelogCache = new HashMap<>(); public AddonChangelog(AddonUpdateObject object) { + List availableVersions = object.data.availableVersions(); RemoteAddon.Version targetVersion = object.data.targetVersion(); - this.setHeading(new HBox(new Label(i18n("addon.changelog") + " - " + targetVersion.name()))); + Label headingLabel = new Label(i18n("addon.changelog")); + this.setHeading(new HBox(8, headingLabel)); VBox box = new VBox(8); box.setPadding(new Insets(8)); + // Version selector ComboBox + JFXComboBox versionComboBox = new JFXComboBox<>(); + versionComboBox.getItems().setAll(availableVersions); + versionComboBox.getSelectionModel().select(targetVersion); + versionComboBox.setConverter(new StringConverter() { + @Override + public String toString(RemoteAddon.Version version) { + return version == null ? "" : version.name(); + } + + @Override + public RemoteAddon.Version fromString(String string) { + return null; + } + }); + versionComboBox.prefWidthProperty().bind(box.widthProperty()); + + Label selectVersionLabel = new Label(i18n("addon.changelog.other_versions")); + HBox versionSelector = new HBox(8, selectVersionLabel, versionComboBox); + versionSelector.setAlignment(Pos.CENTER_LEFT); + versionSelector.setPadding(new Insets(0, 0, 4, 0)); + box.getChildren().add(versionSelector); + SpinnerPane spinnerPane = new SpinnerPane(); ScrollPane scrollPane = new ScrollPane(); scrollPane.setFitToWidth(true); scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); FXUtils.setOverflowHidden(scrollPane, 8); - loadChangelog(object, spinnerPane, scrollPane); - spinnerPane.setOnFailedAction(e -> loadChangelog(object, spinnerPane, scrollPane)); + loadChangelog(object, versionComboBox.getSelectionModel().getSelectedItem(), spinnerPane, scrollPane); + spinnerPane.setOnFailedAction(e -> loadChangelog(object, versionComboBox.getSelectionModel().getSelectedItem(), spinnerPane, scrollPane)); + + versionComboBox.getSelectionModel().selectedItemProperty().addListener((obs, oldVersion, newVersion) -> { + if (newVersion != null) { + loadChangelog(object, newVersion, spinnerPane, scrollPane); + } + }); spinnerPane.setContent(scrollPane); box.getChildren().add(spinnerPane); @@ -340,23 +373,25 @@ public AddonChangelog(AddonUpdateObject object) { onEscPressed(this, closeButton::fire); } - private void loadChangelog(AddonUpdateObject object, SpinnerPane spinnerPane, ScrollPane scrollPane) { + private void loadChangelog(AddonUpdateObject object, RemoteAddon.Version version, SpinnerPane spinnerPane, ScrollPane scrollPane) { + if (version == null) return; spinnerPane.setLoading(true); RemoteAddonRepository repo = object.data.source().getRepoForType(object.data.repoType()); Task.supplyAsync(() -> { - if (object.changelog != null) { - return object.changelog; + String cached = changelogCache.get(version.versionId()); + if (cached != null) { + return cached; } - RemoteAddon.Version version = object.data.targetVersion(); if (repo == null) return null; return StringUtils.convertToHtml( repo.getAddonChangelog(DownloadProviders.getDownloadProvider(), version.projectId(), version.versionId()), - "238222".equals(object.data.targetVersion().projectId()) + "238222".equals(version.projectId()) ); }).whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null) { - object.changelog = StringUtils.isNotBlank(result) ? result : i18n("addon.changelog.empty"); - scrollPane.setContent(FXUtils.renderAddonChangelog(object.changelog, repo == null ? "" : repo.getBaseUrl())); + String html = StringUtils.isNotBlank(result) ? result : i18n("addon.changelog.empty"); + changelogCache.put(version.versionId(), html); + scrollPane.setContent(FXUtils.renderAddonChangelog(html, repo == null ? "" : repo.getBaseUrl())); FXUtils.smoothScrolling(scrollPane); spinnerPane.setFailedReason(null); } else { diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 0e4aa87042..f54adb8daa 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -166,6 +166,7 @@ addon.broken_dependency.desc=This dependency existed before, but it does not exi addon.category=Category addon.changelog=Changelog addon.changelog.empty=Currently no changelog +addon.changelog.other_versions=Other versions addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Release diff --git a/HMCL/src/main/resources/assets/lang/I18N_de.properties b/HMCL/src/main/resources/assets/lang/I18N_de.properties index b31c86d589..b22ef03260 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_de.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_de.properties @@ -165,6 +165,7 @@ addon.broken_dependency.desc=Diese Abhängigkeit existierte vorher, ist aber nic addon.category=Kategorie addon.changelog=Änderungsprotokoll addon.changelog.empty=Derzeit kein Änderungsprotokoll +addon.changelog.other_versions=Andere Versionen addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Veröffentlichung diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index b91bd5fc1d..62e3b59b28 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -142,6 +142,7 @@ addon.broken_dependency.title=所依之壞者 addon.broken_dependency.desc=夫改囊素存於改囊庫,今闕矣,宜易他源。 addon.category=類 addon.changelog=迭更誌 +addon.changelog.other_versions=他版 addon.channel.alpha=預版 addon.channel.beta=試版 addon.channel.release=當版 diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 2c1c09e65f..02ed30cb64 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -172,6 +172,7 @@ addon.broken_dependency.desc=Ця залежність існувала рані addon.category=Категорія addon.changelog=Список змін addon.changelog.empty=Наразі немає списку змін +addon.changelog.other_versions=Інші версії addon.channel.alpha=Альфа addon.channel.beta=Бета addon.channel.release=Реліз diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 75cff38b19..29fcabfa6a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -164,6 +164,7 @@ addon.broken_dependency.desc=該相依內容曾經存在於附加內容下載源 addon.category=類別 addon.changelog=更新日誌 addon.changelog.empty=暫無更新日誌 +addon.changelog.other_versions=查看其他版本 addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Release diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index a75db02bf1..196609b2f0 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -166,6 +166,7 @@ addon.broken_dependency.desc=该前置内容曾经在该附加内容下载源上 addon.category=类别 addon.changelog=更新日志 addon.changelog.empty=暂无更新日志 +addon.changelog.other_versions=查看其他版本 addon.channel.alpha=快照版本 addon.channel.beta=测试版本 addon.channel.release=稳定版本 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java index 0537e4a450..c7215455d0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java @@ -65,7 +65,8 @@ public record AddonUpdate( LocalAddonFile localAddonFile, RemoteAddon.Version currentVersion, RemoteAddon.Version targetVersion, - boolean useRemoteFileName + boolean useRemoteFileName, + List availableVersions ) { } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalModFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalModFile.java index 32984fa3ba..82fa17efa3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalModFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalModFile.java @@ -207,7 +207,7 @@ public AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVe .sorted(Comparator.comparing(RemoteAddon.Version::datePublished).reversed()) .toList(); if (remoteVersions.isEmpty()) return null; - return new AddonUpdate(source, RemoteAddon.Type.MOD, this, currentVersion.get(), remoteVersions.get(0), true); + return new AddonUpdate(source, RemoteAddon.Type.MOD, this, currentVersion.get(), remoteVersions.get(0), true, remoteVersions); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackZipFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackZipFile.java index 87aea908cc..741556ce9c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackZipFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackZipFile.java @@ -101,7 +101,7 @@ public AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVe .sorted(Comparator.comparing(RemoteAddon.Version::datePublished).reversed()) .toList(); if (remoteVersions.isEmpty()) return null; - return new AddonUpdate(source, RemoteAddon.Type.RESOURCE_PACK, this, currentVersion.get(), remoteVersions.get(0), false); + return new AddonUpdate(source, RemoteAddon.Type.RESOURCE_PACK, this, currentVersion.get(), remoteVersions.get(0), false, remoteVersions); } } From 32b922eb7067767d0614a598d5f4afb497d904da Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:11:28 +0800 Subject: [PATCH 02/19] fix label not visible, simplify string converter --- .../hmcl/ui/instances/AddonUpdatesPage.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index 796dfa557a..01c38b2630 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -317,18 +317,8 @@ public AddonChangelog(AddonUpdateObject object) { JFXComboBox versionComboBox = new JFXComboBox<>(); versionComboBox.getItems().setAll(availableVersions); versionComboBox.getSelectionModel().select(targetVersion); - versionComboBox.setConverter(new StringConverter() { - @Override - public String toString(RemoteAddon.Version version) { - return version == null ? "" : version.name(); - } - - @Override - public RemoteAddon.Version fromString(String string) { - return null; - } - }); - versionComboBox.prefWidthProperty().bind(box.widthProperty()); + versionComboBox.setConverter(FXUtils.stringConverter(RemoteAddon.Version::name)); + HBox.setHgrow(versionComboBox, Priority.ALWAYS); Label selectVersionLabel = new Label(i18n("addon.changelog.other_versions")); HBox versionSelector = new HBox(8, selectVersionLabel, versionComboBox); From ba1f6b5df7d6c1b9c66a4f71a22c490202dd8448 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:28:23 +0800 Subject: [PATCH 03/19] make text a bit more clear, ensure label will not get too long Co-authored-by: mimo-v2.5 --- .../org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java | 5 ++++- HMCL/src/main/resources/assets/lang/I18N.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_de.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_lzh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_uk.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 2 +- 7 files changed, 10 insertions(+), 7 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index 01c38b2630..abf5919096 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -320,7 +320,10 @@ public AddonChangelog(AddonUpdateObject object) { versionComboBox.setConverter(FXUtils.stringConverter(RemoteAddon.Version::name)); HBox.setHgrow(versionComboBox, Priority.ALWAYS); - Label selectVersionLabel = new Label(i18n("addon.changelog.other_versions")); + Label selectVersionLabel = new Label(i18n("addon.changelog.view_other_log")); + selectVersionLabel.maxWidthProperty().bind(box.widthProperty().divide(3)); + FXUtils.showTooltipWhenTruncated(selectVersionLabel); + HBox versionSelector = new HBox(8, selectVersionLabel, versionComboBox); versionSelector.setAlignment(Pos.CENTER_LEFT); versionSelector.setPadding(new Insets(0, 0, 4, 0)); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index f54adb8daa..f60310f3d2 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -166,7 +166,7 @@ addon.broken_dependency.desc=This dependency existed before, but it does not exi addon.category=Category addon.changelog=Changelog addon.changelog.empty=Currently no changelog -addon.changelog.other_versions=Other versions +addon.changelog.view_other_log=View other versions' changelog addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Release diff --git a/HMCL/src/main/resources/assets/lang/I18N_de.properties b/HMCL/src/main/resources/assets/lang/I18N_de.properties index b22ef03260..e588a9dccf 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_de.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_de.properties @@ -165,7 +165,7 @@ addon.broken_dependency.desc=Diese Abhängigkeit existierte vorher, ist aber nic addon.category=Kategorie addon.changelog=Änderungsprotokoll addon.changelog.empty=Derzeit kein Änderungsprotokoll -addon.changelog.other_versions=Andere Versionen +addon.changelog.view_other_log=Änderungsprotokolle anderer Versionen addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Veröffentlichung diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 62e3b59b28..720a3241e6 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -142,7 +142,7 @@ addon.broken_dependency.title=所依之壞者 addon.broken_dependency.desc=夫改囊素存於改囊庫,今闕矣,宜易他源。 addon.category=類 addon.changelog=迭更誌 -addon.changelog.other_versions=他版 +addon.changelog.view_other_log=覽他版之迭更 addon.channel.alpha=預版 addon.channel.beta=試版 addon.channel.release=當版 diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 02ed30cb64..9ae817c0f3 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -172,7 +172,7 @@ addon.broken_dependency.desc=Ця залежність існувала рані addon.category=Категорія addon.changelog=Список змін addon.changelog.empty=Наразі немає списку змін -addon.changelog.other_versions=Інші версії +addon.changelog.view_other_log=Зміни інших версій addon.channel.alpha=Альфа addon.channel.beta=Бета addon.channel.release=Реліз diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 29fcabfa6a..028d043821 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -164,7 +164,7 @@ addon.broken_dependency.desc=該相依內容曾經存在於附加內容下載源 addon.category=類別 addon.changelog=更新日誌 addon.changelog.empty=暫無更新日誌 -addon.changelog.other_versions=查看其他版本 +addon.changelog.view_other_log=查看其它版本的日誌 addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Release diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 196609b2f0..a8c223efcb 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -166,7 +166,7 @@ addon.broken_dependency.desc=该前置内容曾经在该附加内容下载源上 addon.category=类别 addon.changelog=更新日志 addon.changelog.empty=暂无更新日志 -addon.changelog.other_versions=查看其他版本 +addon.changelog.view_other_log=查看其它版本的日志 addon.channel.alpha=快照版本 addon.channel.beta=测试版本 addon.channel.release=稳定版本 From 7731d98d37e367da6929e128ac55793f9c4a7a6a Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:37:12 +0800 Subject: [PATCH 04/19] cleanup --- .../java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index abf5919096..90c3b0f740 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -29,7 +29,6 @@ import javafx.geometry.Pos; import javafx.scene.control.*; import javafx.scene.layout.*; -import javafx.util.StringConverter; import org.jackhuang.hmcl.addon.LocalAddonFile; import org.jackhuang.hmcl.addon.LocalAddonManager; import org.jackhuang.hmcl.addon.RemoteAddon; @@ -217,7 +216,6 @@ private static final class AddonUpdateObject { final StringProperty currentVersion = new SimpleStringProperty(); final StringProperty targetVersion = new SimpleStringProperty(); final StringProperty source = new SimpleStringProperty(); - String changelog = null; public AddonUpdateObject(LocalAddonFile.AddonUpdate data) { this.data = data; From bea4dfb22f14f605218000ca1f90e919acf5dba5 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:58:01 +0800 Subject: [PATCH 05/19] fix i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `I18N_zh_CN.properties: misspelled '其它' should be replaced by '其他'` from `gradle checkTranslations` --- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index a8c223efcb..10b9202711 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -166,7 +166,7 @@ addon.broken_dependency.desc=该前置内容曾经在该附加内容下载源上 addon.category=类别 addon.changelog=更新日志 addon.changelog.empty=暂无更新日志 -addon.changelog.view_other_log=查看其它版本的日志 +addon.changelog.view_other_log=查看其他版本的日志 addon.channel.alpha=快照版本 addon.channel.beta=测试版本 addon.channel.release=稳定版本 From 1127816611f2d14a9273c5a6625bb7c7eb9672ac Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:41:27 +0800 Subject: [PATCH 06/19] fix i18n (1) --- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 028d043821..a3757b0489 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -164,7 +164,7 @@ addon.broken_dependency.desc=該相依內容曾經存在於附加內容下載源 addon.category=類別 addon.changelog=更新日誌 addon.changelog.empty=暫無更新日誌 -addon.changelog.view_other_log=查看其它版本的日誌 +addon.changelog.view_other_log=查看其他版本的日誌 addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Release From c21c1417783b13c83595a23bbd62802dc2045016 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Thu, 27 Aug 2026 22:37:26 +0800 Subject: [PATCH 07/19] "View other versions' changelog" -> "Mod Version" --- .../java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java | 2 -- HMCL/src/main/resources/assets/lang/I18N.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_de.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_lzh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_uk.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 2 +- 7 files changed, 6 insertions(+), 8 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index 90c3b0f740..f342e970ba 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -319,8 +319,6 @@ public AddonChangelog(AddonUpdateObject object) { HBox.setHgrow(versionComboBox, Priority.ALWAYS); Label selectVersionLabel = new Label(i18n("addon.changelog.view_other_log")); - selectVersionLabel.maxWidthProperty().bind(box.widthProperty().divide(3)); - FXUtils.showTooltipWhenTruncated(selectVersionLabel); HBox versionSelector = new HBox(8, selectVersionLabel, versionComboBox); versionSelector.setAlignment(Pos.CENTER_LEFT); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index f60310f3d2..01efad096e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -166,7 +166,7 @@ addon.broken_dependency.desc=This dependency existed before, but it does not exi addon.category=Category addon.changelog=Changelog addon.changelog.empty=Currently no changelog -addon.changelog.view_other_log=View other versions' changelog +addon.changelog.view_other_log=Mod Version addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Release diff --git a/HMCL/src/main/resources/assets/lang/I18N_de.properties b/HMCL/src/main/resources/assets/lang/I18N_de.properties index e588a9dccf..18ce29b910 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_de.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_de.properties @@ -165,7 +165,7 @@ addon.broken_dependency.desc=Diese Abhängigkeit existierte vorher, ist aber nic addon.category=Kategorie addon.changelog=Änderungsprotokoll addon.changelog.empty=Derzeit kein Änderungsprotokoll -addon.changelog.view_other_log=Änderungsprotokolle anderer Versionen +addon.changelog.view_other_log=Mod-Version addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Veröffentlichung diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 720a3241e6..5eac1ed727 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -142,7 +142,7 @@ addon.broken_dependency.title=所依之壞者 addon.broken_dependency.desc=夫改囊素存於改囊庫,今闕矣,宜易他源。 addon.category=類 addon.changelog=迭更誌 -addon.changelog.view_other_log=覽他版之迭更 +addon.changelog.view_other_log=模組版本 addon.channel.alpha=預版 addon.channel.beta=試版 addon.channel.release=當版 diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 9ae817c0f3..c4adf3ccd2 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -172,7 +172,7 @@ addon.broken_dependency.desc=Ця залежність існувала рані addon.category=Категорія addon.changelog=Список змін addon.changelog.empty=Наразі немає списку змін -addon.changelog.view_other_log=Зміни інших версій +addon.changelog.view_other_log=Версія моду addon.channel.alpha=Альфа addon.channel.beta=Бета addon.channel.release=Реліз diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index a3757b0489..77fb60f552 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -164,7 +164,7 @@ addon.broken_dependency.desc=該相依內容曾經存在於附加內容下載源 addon.category=類別 addon.changelog=更新日誌 addon.changelog.empty=暫無更新日誌 -addon.changelog.view_other_log=查看其他版本的日誌 +addon.changelog.view_other_log=模組版本 addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Release diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 10b9202711..e529738aa8 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -166,7 +166,7 @@ addon.broken_dependency.desc=该前置内容曾经在该附加内容下载源上 addon.category=类别 addon.changelog=更新日志 addon.changelog.empty=暂无更新日志 -addon.changelog.view_other_log=查看其他版本的日志 +addon.changelog.view_other_log=模组版本 addon.channel.alpha=快照版本 addon.channel.beta=测试版本 addon.channel.release=稳定版本 From 3d6d4431b70f44be1b75aa6fd07bad74c906df3b Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:09:53 +0800 Subject: [PATCH 08/19] "Mod Version" -> "Select version" Co-authored-by: mimo-v2.5 --- HMCL/src/main/resources/assets/lang/I18N.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_de.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_lzh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_uk.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 01efad096e..7784313888 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -166,7 +166,7 @@ addon.broken_dependency.desc=This dependency existed before, but it does not exi addon.category=Category addon.changelog=Changelog addon.changelog.empty=Currently no changelog -addon.changelog.view_other_log=Mod Version +addon.changelog.view_other_log=Select version addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Release diff --git a/HMCL/src/main/resources/assets/lang/I18N_de.properties b/HMCL/src/main/resources/assets/lang/I18N_de.properties index 18ce29b910..9229a06dfb 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_de.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_de.properties @@ -165,7 +165,7 @@ addon.broken_dependency.desc=Diese Abhängigkeit existierte vorher, ist aber nic addon.category=Kategorie addon.changelog=Änderungsprotokoll addon.changelog.empty=Derzeit kein Änderungsprotokoll -addon.changelog.view_other_log=Mod-Version +addon.changelog.view_other_log=Version auswählen addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Veröffentlichung diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 5eac1ed727..994d97000e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -142,7 +142,7 @@ addon.broken_dependency.title=所依之壞者 addon.broken_dependency.desc=夫改囊素存於改囊庫,今闕矣,宜易他源。 addon.category=類 addon.changelog=迭更誌 -addon.changelog.view_other_log=模組版本 +addon.changelog.view_other_log=擇版 addon.channel.alpha=預版 addon.channel.beta=試版 addon.channel.release=當版 diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index c4adf3ccd2..26f2273a47 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -172,7 +172,7 @@ addon.broken_dependency.desc=Ця залежність існувала рані addon.category=Категорія addon.changelog=Список змін addon.changelog.empty=Наразі немає списку змін -addon.changelog.view_other_log=Версія моду +addon.changelog.view_other_log=Оберіть версію addon.channel.alpha=Альфа addon.channel.beta=Бета addon.channel.release=Реліз diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 77fb60f552..259ed65ac9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -164,7 +164,7 @@ addon.broken_dependency.desc=該相依內容曾經存在於附加內容下載源 addon.category=類別 addon.changelog=更新日誌 addon.changelog.empty=暫無更新日誌 -addon.changelog.view_other_log=模組版本 +addon.changelog.view_other_log=選擇版本 addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Release diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index e529738aa8..8ac393f87a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -166,7 +166,7 @@ addon.broken_dependency.desc=该前置内容曾经在该附加内容下载源上 addon.category=类别 addon.changelog=更新日志 addon.changelog.empty=暂无更新日志 -addon.changelog.view_other_log=模组版本 +addon.changelog.view_other_log=选择版本 addon.channel.alpha=快照版本 addon.channel.beta=测试版本 addon.channel.release=稳定版本 From e827fcb29fbf1c2ee5bba54f79b6985ca3494c30 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Sat, 29 Aug 2026 20:13:08 +0800 Subject: [PATCH 09/19] implement "select target version" --- .../ui/instances/AddonCheckUpdatesTask.java | 2 +- .../hmcl/ui/instances/AddonUpdatesPage.java | 77 ++++++++----------- .../jackhuang/hmcl/addon/LocalAddonFile.java | 8 +- .../hmcl/addon/mod/LocalModFile.java | 2 +- .../resourcepack/ResourcePackZipFile.java | 2 +- 5 files changed, 38 insertions(+), 53 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonCheckUpdatesTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonCheckUpdatesTask.java index 531bbaa94f..bada188589 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonCheckUpdatesTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonCheckUpdatesTask.java @@ -50,7 +50,7 @@ public AddonCheckUpdatesTask(DownloadProvider downloadProvider, String gameVersi continue; } - if (candidate == null || candidate.targetVersion().datePublished().isBefore(update.targetVersion().datePublished())) { + if (candidate == null || candidate.latestAvailableVersion().datePublished().isBefore(update.latestAvailableVersion().datePublished())) { candidate = update; } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index f342e970ba..6384d19272 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -153,7 +153,6 @@ private void updateFiles() { localAddonManager.getDirectory(), objects.stream() .filter(AddonUpdateObject::isEnabled) - .map(AddonUpdateObject::getData) .toList() ); Controllers.taskDialog( @@ -188,7 +187,7 @@ private void exportList() { for (int i = 0; i < objects.size(); i++) { csvTable.set(0, i + 1, objects.get(i).fileName.get()); csvTable.set(1, i + 1, objects.get(i).currentVersion.get()); - csvTable.set(2, i + 1, objects.get(i).targetVersion.get()); + csvTable.set(2, i + 1, objects.get(i).targetVersion.getValue()); csvTable.set(3, i + 1, objects.get(i).source.get()); } @@ -211,26 +210,26 @@ public ReadOnlyObjectWrapper stateProperty() { private static final class AddonUpdateObject { final LocalAddonFile.AddonUpdate data; - final BooleanProperty enabled = new SimpleBooleanProperty(); - final StringProperty fileName = new SimpleStringProperty(); - final StringProperty currentVersion = new SimpleStringProperty(); - final StringProperty targetVersion = new SimpleStringProperty(); - final StringProperty source = new SimpleStringProperty(); + final ObjectProperty targetVersionObject; + final BooleanProperty enabled; + final StringProperty fileName; + final StringProperty currentVersion; + final ObservableValue targetVersion; + final StringProperty source; public AddonUpdateObject(LocalAddonFile.AddonUpdate data) { this.data = data; + this.targetVersionObject = new SimpleObjectProperty<>(data.latestAvailableVersion()); - enabled.set(!data.localAddonFile().isDisabled()); - fileName.set(data.localAddonFile().getFileName()); - currentVersion.set(data.currentVersion().version()); - targetVersion.set(data.targetVersion().version()); - switch (data.currentVersion().self().getSource()) { - case CURSEFORGE: - source.set(i18n("addon.curseforge")); - break; - case MODRINTH: - source.set(i18n("addon.modrinth")); - } + enabled = new SimpleBooleanProperty(!data.localAddonFile().isDisabled()); + + fileName = new SimpleStringProperty(data.localAddonFile().getFileName()); + currentVersion = new SimpleStringProperty(data.currentVersion().version()); + targetVersion = this.targetVersionObject.map(RemoteAddon.Version::version); + source = switch (data.currentVersion().self().getSource()) { + case CURSEFORGE -> new SimpleStringProperty(i18n("addon.curseforge")); + case MODRINTH -> new SimpleStringProperty(i18n("addon.modrinth")); + }; } public LocalAddonFile.AddonUpdate getData() { @@ -253,49 +252,33 @@ public String getFileName() { return fileName.get(); } - public StringProperty fileNameProperty() { + public ReadOnlyStringProperty fileNameProperty() { return fileName; } - public void setFileName(String fileName) { - this.fileName.set(fileName); - } - public String getCurrentVersion() { return currentVersion.get(); } - public StringProperty currentVersionProperty() { + public ReadOnlyStringProperty currentVersionProperty() { return currentVersion; } - public void setCurrentVersion(String currentVersion) { - this.currentVersion.set(currentVersion); - } - public String getTargetVersion() { - return targetVersion.get(); + return targetVersion.getValue(); } - public StringProperty targetVersionProperty() { + public ObservableValue targetVersionProperty() { return targetVersion; } - public void setTargetVersion(String targetVersion) { - this.targetVersion.set(targetVersion); - } - public String getSource() { return source.get(); } - public StringProperty sourceProperty() { + public ReadOnlyStringProperty sourceProperty() { return source; } - - public void setSource(String source) { - this.source.set(source); - } } private static final class AddonChangelog extends JFXDialogLayout { @@ -303,7 +286,6 @@ private static final class AddonChangelog extends JFXDialogLayout { public AddonChangelog(AddonUpdateObject object) { List availableVersions = object.data.availableVersions(); - RemoteAddon.Version targetVersion = object.data.targetVersion(); Label headingLabel = new Label(i18n("addon.changelog")); this.setHeading(new HBox(8, headingLabel)); @@ -314,7 +296,8 @@ public AddonChangelog(AddonUpdateObject object) { // Version selector ComboBox JFXComboBox versionComboBox = new JFXComboBox<>(); versionComboBox.getItems().setAll(availableVersions); - versionComboBox.getSelectionModel().select(targetVersion); + versionComboBox.getSelectionModel().select(object.targetVersionObject.get()); + FXUtils.onChange(versionComboBox.getSelectionModel().selectedItemProperty(), object.targetVersionObject::set); versionComboBox.setConverter(FXUtils.stringConverter(RemoteAddon.Version::name)); HBox.setHgrow(versionComboBox, Priority.ALWAYS); @@ -393,7 +376,7 @@ private void loadChangelog(AddonUpdateObject object, RemoteAddon.Version version private void loadVersionPageUrl(AddonUpdateObject object, JFXHyperlink button) { Task.supplyAsync(() -> { RemoteAddonRepository repo = object.data.source().getRepoForType(object.data.repoType()); - return repo == null ? null : repo.getVersionPageUrl(object.data.targetVersion()); + return repo == null ? null : repo.getVersionPageUrl(object.targetVersionObject.get()); }).whenComplete(Schedulers.javafx(), (result, exception) -> { if (exception == null && StringUtils.isNotBlank(result)) { button.setExternalLink(result); @@ -409,21 +392,21 @@ public static class AddonUpdateTask extends Task { private final Collection> dependents; private final List failedAddons = new ArrayList<>(); - AddonUpdateTask(Path addonDirectory, List addons) { + AddonUpdateTask(Path addonDirectory, List addons) { setStage("addon.check_update.confirm"); getProperties().put("total", addons.size()); this.dependents = new ArrayList<>(); - for (LocalAddonFile.AddonUpdate addon : addons) { - LocalAddonFile local = addon.localAddonFile(); - RemoteAddon.Version remote = addon.targetVersion(); + for (var addon : addons) { + LocalAddonFile local = addon.data.localAddonFile(); + RemoteAddon.Version remote = addon.targetVersionObject.get(); boolean isDisabled = local.isDisabled(); String originalFileName = local.getFile().getFileName().toString(); dependents.add(Task .runAsync(Schedulers.javafx(), () -> local.setOld(true)) .thenComposeAsync(() -> { - String fileName = addon.useRemoteFileName() ? remote.file().filename() : originalFileName; + String fileName = addon.data.useRemoteFileName() ? remote.file().filename() : originalFileName; if (isDisabled) fileName = StringUtils.addSuffix(fileName, LocalAddonManager.DISABLED_EXTENSION); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java index c7215455d0..cba21ac04d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java @@ -64,10 +64,12 @@ public record AddonUpdate( RemoteAddon.Type repoType, LocalAddonFile localAddonFile, RemoteAddon.Version currentVersion, - RemoteAddon.Version targetVersion, - boolean useRemoteFileName, - List availableVersions + List availableVersions, + boolean useRemoteFileName ) { + public RemoteAddon.Version latestAvailableVersion() { + return availableVersions.getFirst(); + } } public static class Description { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalModFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalModFile.java index 82fa17efa3..5d14d68455 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalModFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/mod/LocalModFile.java @@ -207,7 +207,7 @@ public AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVe .sorted(Comparator.comparing(RemoteAddon.Version::datePublished).reversed()) .toList(); if (remoteVersions.isEmpty()) return null; - return new AddonUpdate(source, RemoteAddon.Type.MOD, this, currentVersion.get(), remoteVersions.get(0), true, remoteVersions); + return new AddonUpdate(source, RemoteAddon.Type.MOD, this, currentVersion.get(), remoteVersions, true); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackZipFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackZipFile.java index 741556ce9c..b3f1ade2b8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackZipFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/resourcepack/ResourcePackZipFile.java @@ -101,7 +101,7 @@ public AddonUpdate checkUpdates(DownloadProvider downloadProvider, String gameVe .sorted(Comparator.comparing(RemoteAddon.Version::datePublished).reversed()) .toList(); if (remoteVersions.isEmpty()) return null; - return new AddonUpdate(source, RemoteAddon.Type.RESOURCE_PACK, this, currentVersion.get(), remoteVersions.get(0), false, remoteVersions); + return new AddonUpdate(source, RemoteAddon.Type.RESOURCE_PACK, this, currentVersion.get(), remoteVersions, false); } } From ed787be04b22e1c71b38e581707ed54cb41c58f6 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Sat, 29 Aug 2026 20:14:26 +0800 Subject: [PATCH 10/19] update title "Changelog" -> "Select target version" Co-authored-by: mimo-v2.5 --- HMCL/src/main/resources/assets/lang/I18N.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_de.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_lzh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_uk.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 7784313888..ef5d9a9536 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -164,7 +164,7 @@ account.username=Username addon.broken_dependency.title=Broken dependency addon.broken_dependency.desc=This dependency existed before, but it does not exist anymore. Try using another download source. addon.category=Category -addon.changelog=Changelog +addon.changelog=Select target version addon.changelog.empty=Currently no changelog addon.changelog.view_other_log=Select version addon.channel.alpha=Alpha diff --git a/HMCL/src/main/resources/assets/lang/I18N_de.properties b/HMCL/src/main/resources/assets/lang/I18N_de.properties index 9229a06dfb..d666c8da81 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_de.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_de.properties @@ -163,7 +163,7 @@ account.username=Benutzername addon.broken_dependency.title=Fehlerhafte Abhängigkeit addon.broken_dependency.desc=Diese Abhängigkeit existierte vorher, ist aber nicht mehr vorhanden. Versuchen Sie, eine andere Download-Quelle zu verwenden. addon.category=Kategorie -addon.changelog=Änderungsprotokoll +addon.changelog=Zielversion auswählen addon.changelog.empty=Derzeit kein Änderungsprotokoll addon.changelog.view_other_log=Version auswählen addon.channel.alpha=Alpha diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 994d97000e..2c6ef2b033 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -141,7 +141,7 @@ account.username=戶名 addon.broken_dependency.title=所依之壞者 addon.broken_dependency.desc=夫改囊素存於改囊庫,今闕矣,宜易他源。 addon.category=類 -addon.changelog=迭更誌 +addon.changelog=擇標的之版 addon.changelog.view_other_log=擇版 addon.channel.alpha=預版 addon.channel.beta=試版 diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 26f2273a47..ca820fec99 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -170,7 +170,7 @@ account.username=Ім'я користувача addon.broken_dependency.title=Зламана залежність addon.broken_dependency.desc=Ця залежність існувала раніше, але тепер її немає. Спробуйте використовувати інше джерело завантаження. addon.category=Категорія -addon.changelog=Список змін +addon.changelog=Оберіть цільову версію addon.changelog.empty=Наразі немає списку змін addon.changelog.view_other_log=Оберіть версію addon.channel.alpha=Альфа diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 259ed65ac9..1857f7e2fd 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -162,7 +162,7 @@ account.username=使用者名稱 addon.broken_dependency.title=損壞的相依內容 addon.broken_dependency.desc=該相依內容曾經存在於附加內容下載源中,但現在已被刪除,請嘗試其他下載源。 addon.category=類別 -addon.changelog=更新日誌 +addon.changelog=選擇目標版本 addon.changelog.empty=暫無更新日誌 addon.changelog.view_other_log=選擇版本 addon.channel.alpha=Alpha diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 8ac393f87a..b12ec27a30 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -164,7 +164,7 @@ account.username=用户名 addon.broken_dependency.title=损坏的前置内容 addon.broken_dependency.desc=该前置内容曾经在该附加内容下载源上存在过,但现在被删除了。换个下载源试试吧。 addon.category=类别 -addon.changelog=更新日志 +addon.changelog=选择目标版本 addon.changelog.empty=暂无更新日志 addon.changelog.view_other_log=选择版本 addon.channel.alpha=快照版本 From e2a1df00e2d62e4e7dad1f72c74c2a75d9d043f6 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Sat, 29 Aug 2026 20:27:20 +0800 Subject: [PATCH 11/19] some unification --- .../hmcl/ui/instances/AddonUpdatesPage.java | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index 6384d19272..6de7282388 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -21,6 +21,7 @@ import com.jfoenix.controls.JFXCheckBox; import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXDialogLayout; +import com.sun.javafx.binding.StringConstant; import javafx.beans.property.*; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; @@ -86,18 +87,18 @@ public AddonUpdatesPage(LocalAddonManager localAddonManager, List fileNameColumn = new TableColumn<>(i18n("addon.check_update.file")); fileNameColumn.setPrefWidth(180); - setupCellValueFactory(fileNameColumn, AddonUpdateObject::fileNameProperty); + setupCellValueFactory(fileNameColumn, AddonUpdateObject::fileNameValue); TableColumn currentVersionColumn = new TableColumn<>(i18n("addon.check_update.current_version")); currentVersionColumn.setPrefWidth(180); - setupCellValueFactory(currentVersionColumn, AddonUpdateObject::currentVersionProperty); + setupCellValueFactory(currentVersionColumn, AddonUpdateObject::currentVersionValue); TableColumn targetVersionColumn = new TableColumn<>(i18n("addon.check_update.target_version")); targetVersionColumn.setPrefWidth(180); - setupCellValueFactory(targetVersionColumn, AddonUpdateObject::targetVersionProperty); + setupCellValueFactory(targetVersionColumn, AddonUpdateObject::targetVersionValue); TableColumn sourceColumn = new TableColumn<>(i18n("addon.check_update.source")); - setupCellValueFactory(sourceColumn, AddonUpdateObject::sourceProperty); + setupCellValueFactory(sourceColumn, AddonUpdateObject::sourceValue); TableColumn changelogColumn = new TableColumn<>(i18n("addon.changelog")); { @@ -185,10 +186,10 @@ private void exportList() { csvTable.set(3, 0, "Update Source"); for (int i = 0; i < objects.size(); i++) { - csvTable.set(0, i + 1, objects.get(i).fileName.get()); - csvTable.set(1, i + 1, objects.get(i).currentVersion.get()); - csvTable.set(2, i + 1, objects.get(i).targetVersion.getValue()); - csvTable.set(3, i + 1, objects.get(i).source.get()); + csvTable.set(0, i + 1, objects.get(i).getFileName()); + csvTable.set(1, i + 1, objects.get(i).getCurrentVersion()); + csvTable.set(2, i + 1, objects.get(i).getTargetVersion()); + csvTable.set(3, i + 1, objects.get(i).getSource()); } csvTable.write(path); @@ -212,10 +213,10 @@ private static final class AddonUpdateObject { final LocalAddonFile.AddonUpdate data; final ObjectProperty targetVersionObject; final BooleanProperty enabled; - final StringProperty fileName; - final StringProperty currentVersion; + final ObservableValue fileName; + final ObservableValue currentVersion; final ObservableValue targetVersion; - final StringProperty source; + final ObservableValue source; public AddonUpdateObject(LocalAddonFile.AddonUpdate data) { this.data = data; @@ -223,12 +224,12 @@ public AddonUpdateObject(LocalAddonFile.AddonUpdate data) { enabled = new SimpleBooleanProperty(!data.localAddonFile().isDisabled()); - fileName = new SimpleStringProperty(data.localAddonFile().getFileName()); - currentVersion = new SimpleStringProperty(data.currentVersion().version()); + fileName = StringConstant.valueOf(data.localAddonFile().getFileName()); + currentVersion = StringConstant.valueOf(data.currentVersion().version()); targetVersion = this.targetVersionObject.map(RemoteAddon.Version::version); source = switch (data.currentVersion().self().getSource()) { - case CURSEFORGE -> new SimpleStringProperty(i18n("addon.curseforge")); - case MODRINTH -> new SimpleStringProperty(i18n("addon.modrinth")); + case CURSEFORGE -> StringConstant.valueOf(i18n("addon.curseforge")); + case MODRINTH -> StringConstant.valueOf(i18n("addon.modrinth")); }; } @@ -249,18 +250,18 @@ public void setEnabled(boolean enabled) { } public String getFileName() { - return fileName.get(); + return fileName.getValue(); } - public ReadOnlyStringProperty fileNameProperty() { + public ObservableValue fileNameValue() { return fileName; } public String getCurrentVersion() { - return currentVersion.get(); + return currentVersion.getValue(); } - public ReadOnlyStringProperty currentVersionProperty() { + public ObservableValue currentVersionValue() { return currentVersion; } @@ -268,15 +269,15 @@ public String getTargetVersion() { return targetVersion.getValue(); } - public ObservableValue targetVersionProperty() { + public ObservableValue targetVersionValue() { return targetVersion; } public String getSource() { - return source.get(); + return source.getValue(); } - public ReadOnlyStringProperty sourceProperty() { + public ObservableValue sourceValue() { return source; } } From d6fe74eb7abbc48e371b17ab430e721146d4ff82 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Sat, 29 Aug 2026 20:34:58 +0800 Subject: [PATCH 12/19] fine, `.getFirst()` not available until Java 21 --- .../src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java index cba21ac04d..70e588a00d 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/LocalAddonFile.java @@ -68,7 +68,7 @@ public record AddonUpdate( boolean useRemoteFileName ) { public RemoteAddon.Version latestAvailableVersion() { - return availableVersions.getFirst(); + return availableVersions.get(0); } } From c5c06d38421fad674829b14799c1a63ce567e5de Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Sun, 6 Sep 2026 20:33:41 +0800 Subject: [PATCH 13/19] back to property, remove `property.map(function)` call --- .../hmcl/ui/instances/AddonUpdatesPage.java | 75 +++++++++++-------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index 6de7282388..4723166319 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -21,7 +21,6 @@ import com.jfoenix.controls.JFXCheckBox; import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXDialogLayout; -import com.sun.javafx.binding.StringConstant; import javafx.beans.property.*; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; @@ -87,18 +86,18 @@ public AddonUpdatesPage(LocalAddonManager localAddonManager, List fileNameColumn = new TableColumn<>(i18n("addon.check_update.file")); fileNameColumn.setPrefWidth(180); - setupCellValueFactory(fileNameColumn, AddonUpdateObject::fileNameValue); + setupCellValueFactory(fileNameColumn, AddonUpdateObject::fileNameProperty); TableColumn currentVersionColumn = new TableColumn<>(i18n("addon.check_update.current_version")); currentVersionColumn.setPrefWidth(180); - setupCellValueFactory(currentVersionColumn, AddonUpdateObject::currentVersionValue); + setupCellValueFactory(currentVersionColumn, AddonUpdateObject::currentVersionProperty); TableColumn targetVersionColumn = new TableColumn<>(i18n("addon.check_update.target_version")); targetVersionColumn.setPrefWidth(180); - setupCellValueFactory(targetVersionColumn, AddonUpdateObject::targetVersionValue); + setupCellValueFactory(targetVersionColumn, AddonUpdateObject::targetVersionProperty); TableColumn sourceColumn = new TableColumn<>(i18n("addon.check_update.source")); - setupCellValueFactory(sourceColumn, AddonUpdateObject::sourceValue); + setupCellValueFactory(sourceColumn, AddonUpdateObject::sourceProperty); TableColumn changelogColumn = new TableColumn<>(i18n("addon.changelog")); { @@ -211,26 +210,26 @@ public ReadOnlyObjectWrapper stateProperty() { private static final class AddonUpdateObject { final LocalAddonFile.AddonUpdate data; - final ObjectProperty targetVersionObject; - final BooleanProperty enabled; - final ObservableValue fileName; - final ObservableValue currentVersion; - final ObservableValue targetVersion; - final ObservableValue source; + final ObjectProperty targetVersionObject = new SimpleObjectProperty<>(); + final BooleanProperty enabled = new SimpleBooleanProperty(); + final StringProperty fileName = new SimpleStringProperty(); + final StringProperty currentVersion = new SimpleStringProperty(); + final StringProperty targetVersion = new SimpleStringProperty(); + final StringProperty source = new SimpleStringProperty(); public AddonUpdateObject(LocalAddonFile.AddonUpdate data) { this.data = data; - this.targetVersionObject = new SimpleObjectProperty<>(data.latestAvailableVersion()); - - enabled = new SimpleBooleanProperty(!data.localAddonFile().isDisabled()); - - fileName = StringConstant.valueOf(data.localAddonFile().getFileName()); - currentVersion = StringConstant.valueOf(data.currentVersion().version()); - targetVersion = this.targetVersionObject.map(RemoteAddon.Version::version); - source = switch (data.currentVersion().self().getSource()) { - case CURSEFORGE -> StringConstant.valueOf(i18n("addon.curseforge")); - case MODRINTH -> StringConstant.valueOf(i18n("addon.modrinth")); - }; + this.targetVersionObject.set(data.latestAvailableVersion()); + + enabled.set(!data.localAddonFile().isDisabled()); + fileName.set(data.localAddonFile().getFileName()); + currentVersion.set(data.currentVersion().version()); + targetVersion.set(targetVersionObject.get().version()); + FXUtils.onChange(targetVersionObject, (value) -> targetVersion.set(value.version())); + switch (data.currentVersion().self().getSource()) { + case CURSEFORGE -> source.set(i18n("addon.curseforge")); + case MODRINTH -> source.set(i18n("addon.modrinth")); + } } public LocalAddonFile.AddonUpdate getData() { @@ -250,36 +249,52 @@ public void setEnabled(boolean enabled) { } public String getFileName() { - return fileName.getValue(); + return fileName.get(); } - public ObservableValue fileNameValue() { + public StringProperty fileNameProperty() { return fileName; } + public void setFileName(String fileName) { + this.fileName.set(fileName); + } + public String getCurrentVersion() { - return currentVersion.getValue(); + return currentVersion.get(); } - public ObservableValue currentVersionValue() { + public StringProperty currentVersionProperty() { return currentVersion; } + public void setCurrentVersion(String currentVersion) { + this.currentVersion.set(currentVersion); + } + public String getTargetVersion() { - return targetVersion.getValue(); + return targetVersion.get(); } - public ObservableValue targetVersionValue() { + public StringProperty targetVersionProperty() { return targetVersion; } + public void setTargetVersion(String targetVersion) { + this.targetVersion.set(targetVersion); + } + public String getSource() { - return source.getValue(); + return source.get(); } - public ObservableValue sourceValue() { + public StringProperty sourceProperty() { return source; } + + public void setSource(String source) { + this.source.set(source); + } } private static final class AddonChangelog extends JFXDialogLayout { From 9fcb77e67e6974f9f468c78e270d6ca5e7a8baf1 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Sun, 6 Sep 2026 20:41:47 +0800 Subject: [PATCH 14/19] fix remaining problems: `changelog` key, url, load concurrency --- .../hmcl/ui/instances/AddonUpdatesPage.java | 19 +++++++++++++------ .../resources/assets/lang/I18N.properties | 3 ++- .../resources/assets/lang/I18N_de.properties | 3 ++- .../resources/assets/lang/I18N_lzh.properties | 3 ++- .../resources/assets/lang/I18N_uk.properties | 3 ++- .../resources/assets/lang/I18N_zh.properties | 3 ++- .../assets/lang/I18N_zh_CN.properties | 3 ++- 7 files changed, 25 insertions(+), 12 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index 4723166319..0755417da3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -99,7 +99,7 @@ public AddonUpdatesPage(LocalAddonManager localAddonManager, List sourceColumn = new TableColumn<>(i18n("addon.check_update.source")); setupCellValueFactory(sourceColumn, AddonUpdateObject::sourceProperty); - TableColumn changelogColumn = new TableColumn<>(i18n("addon.changelog")); + TableColumn changelogColumn = new TableColumn<>(i18n("addon.select_target_version")); { var oldCellFactory = changelogColumn.getCellFactory(); changelogColumn.setCellFactory(param -> { @@ -303,7 +303,7 @@ private static final class AddonChangelog extends JFXDialogLayout { public AddonChangelog(AddonUpdateObject object) { List availableVersions = object.data.availableVersions(); - Label headingLabel = new Label(i18n("addon.changelog")); + Label headingLabel = new Label(i18n("addon.select_target_version")); this.setHeading(new HBox(8, headingLabel)); VBox box = new VBox(8); @@ -333,9 +333,14 @@ public AddonChangelog(AddonUpdateObject object) { loadChangelog(object, versionComboBox.getSelectionModel().getSelectedItem(), spinnerPane, scrollPane); spinnerPane.setOnFailedAction(e -> loadChangelog(object, versionComboBox.getSelectionModel().getSelectedItem(), spinnerPane, scrollPane)); + JFXHyperlink versionPageBtn = new JFXHyperlink(i18n("mods.url")); + versionPageBtn.setDisable(true); + loadVersionPageUrl(object, versionPageBtn); + versionComboBox.getSelectionModel().selectedItemProperty().addListener((obs, oldVersion, newVersion) -> { if (newVersion != null) { loadChangelog(object, newVersion, spinnerPane, scrollPane); + loadVersionPageUrl(object, versionPageBtn); } }); @@ -345,10 +350,6 @@ public AddonChangelog(AddonUpdateObject object) { this.setBody(box); - JFXHyperlink versionPageBtn = new JFXHyperlink(i18n("mods.url")); - versionPageBtn.setDisable(true); - loadVersionPageUrl(object, versionPageBtn); - JFXButton closeButton = new JFXButton(i18n("button.ok")); closeButton.getStyleClass().add("dialog-accept"); closeButton.setOnAction(e -> fireEvent(new DialogCloseEvent())); @@ -376,6 +377,12 @@ private void loadChangelog(AddonUpdateObject object, RemoteAddon.Version version "238222".equals(version.projectId()) ); }).whenComplete(Schedulers.javafx(), (result, exception) -> { + RemoteAddon.Version currentVersion = object.targetVersionObject.get(); + if (currentVersion != null && !currentVersion.versionId().equals(version.versionId())) { + // Version changed while loading, discard this result. Why's there no 'Task.interrupt()' + return; + } + if (exception == null) { String html = StringUtils.isNotBlank(result) ? result : i18n("addon.changelog.empty"); changelogCache.put(version.versionId(), html); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index ef5d9a9536..468d15e6d3 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -164,9 +164,10 @@ account.username=Username addon.broken_dependency.title=Broken dependency addon.broken_dependency.desc=This dependency existed before, but it does not exist anymore. Try using another download source. addon.category=Category -addon.changelog=Select target version +addon.changelog=Changelog addon.changelog.empty=Currently no changelog addon.changelog.view_other_log=Select version +addon.select_target_version=Select target version addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Release diff --git a/HMCL/src/main/resources/assets/lang/I18N_de.properties b/HMCL/src/main/resources/assets/lang/I18N_de.properties index d666c8da81..682ac30bf2 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_de.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_de.properties @@ -163,9 +163,10 @@ account.username=Benutzername addon.broken_dependency.title=Fehlerhafte Abhängigkeit addon.broken_dependency.desc=Diese Abhängigkeit existierte vorher, ist aber nicht mehr vorhanden. Versuchen Sie, eine andere Download-Quelle zu verwenden. addon.category=Kategorie -addon.changelog=Zielversion auswählen +addon.changelog=Änderungsprotokoll addon.changelog.empty=Derzeit kein Änderungsprotokoll addon.changelog.view_other_log=Version auswählen +addon.select_target_version=Zielversion auswählen addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Veröffentlichung diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 2c6ef2b033..4ea5d7fda4 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -141,8 +141,9 @@ account.username=戶名 addon.broken_dependency.title=所依之壞者 addon.broken_dependency.desc=夫改囊素存於改囊庫,今闕矣,宜易他源。 addon.category=類 -addon.changelog=擇標的之版 +addon.changelog=改囊之誌 addon.changelog.view_other_log=擇版 +addon.select_target_version=擇標的之版 addon.channel.alpha=預版 addon.channel.beta=試版 addon.channel.release=當版 diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index ca820fec99..aabf78bf60 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -170,9 +170,10 @@ account.username=Ім'я користувача addon.broken_dependency.title=Зламана залежність addon.broken_dependency.desc=Ця залежність існувала раніше, але тепер її немає. Спробуйте використовувати інше джерело завантаження. addon.category=Категорія -addon.changelog=Оберіть цільову версію +addon.changelog=Список змін addon.changelog.empty=Наразі немає списку змін addon.changelog.view_other_log=Оберіть версію +addon.select_target_version=Оберіть цільову версію addon.channel.alpha=Альфа addon.channel.beta=Бета addon.channel.release=Реліз diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 1857f7e2fd..e599ea9e6f 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -162,9 +162,10 @@ account.username=使用者名稱 addon.broken_dependency.title=損壞的相依內容 addon.broken_dependency.desc=該相依內容曾經存在於附加內容下載源中,但現在已被刪除,請嘗試其他下載源。 addon.category=類別 -addon.changelog=選擇目標版本 +addon.changelog=更新日誌 addon.changelog.empty=暫無更新日誌 addon.changelog.view_other_log=選擇版本 +addon.select_target_version=選擇目標版本 addon.channel.alpha=Alpha addon.channel.beta=Beta addon.channel.release=Release diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index b12ec27a30..ea1f2cda40 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -164,9 +164,10 @@ account.username=用户名 addon.broken_dependency.title=损坏的前置内容 addon.broken_dependency.desc=该前置内容曾经在该附加内容下载源上存在过,但现在被删除了。换个下载源试试吧。 addon.category=类别 -addon.changelog=选择目标版本 +addon.changelog=更新日志 addon.changelog.empty=暂无更新日志 addon.changelog.view_other_log=选择版本 +addon.select_target_version=选择目标版本 addon.channel.alpha=快照版本 addon.channel.beta=测试版本 addon.channel.release=稳定版本 From cc50e63e7aeb43d52aed2ecbc5f75d46e2f02521 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Wed, 9 Sep 2026 10:53:36 +0800 Subject: [PATCH 15/19] discard url not matching selected version, disable button when loading --- .../hmcl/ui/instances/AddonUpdatesPage.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index 0755417da3..e999efbaf3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -335,12 +335,12 @@ public AddonChangelog(AddonUpdateObject object) { JFXHyperlink versionPageBtn = new JFXHyperlink(i18n("mods.url")); versionPageBtn.setDisable(true); - loadVersionPageUrl(object, versionPageBtn); + loadVersionPageUrl(object, versionPageBtn, versionComboBox.getSelectionModel().getSelectedItem()); versionComboBox.getSelectionModel().selectedItemProperty().addListener((obs, oldVersion, newVersion) -> { if (newVersion != null) { loadChangelog(object, newVersion, spinnerPane, scrollPane); - loadVersionPageUrl(object, versionPageBtn); + loadVersionPageUrl(object, versionPageBtn, newVersion); } }); @@ -396,15 +396,23 @@ private void loadChangelog(AddonUpdateObject object, RemoteAddon.Version version }).start(); } - private void loadVersionPageUrl(AddonUpdateObject object, JFXHyperlink button) { + private void loadVersionPageUrl(AddonUpdateObject object, JFXHyperlink button, RemoteAddon.Version version) { + button.setDisable(true); Task.supplyAsync(() -> { RemoteAddonRepository repo = object.data.source().getRepoForType(object.data.repoType()); - return repo == null ? null : repo.getVersionPageUrl(object.targetVersionObject.get()); + return repo == null ? null : repo.getVersionPageUrl(version); }).whenComplete(Schedulers.javafx(), (result, exception) -> { + RemoteAddon.Version currentVersion = object.targetVersionObject.get(); + if (currentVersion != null && !currentVersion.versionId().equals(version.versionId())) { + // Version changed while loading, discard this result + return; + } + if (exception == null && StringUtils.isNotBlank(result)) { button.setExternalLink(result); button.setDisable(false); } else { + button.setDisable(true); LOG.warning("Failed to load addon version page url", exception); } }).start(); From 90ccfb13533a0bad05676084e764a8cf054aefd3 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Thu, 10 Sep 2026 19:45:25 +0800 Subject: [PATCH 16/19] fix: supersede stale changelog and version-page requests by request id Comparing only the selected version is not enough: selecting A -> B -> A starts two requests for the same version, so a slower older request could still overwrite the newer one, e.g. a late failure hiding a successfully rendered changelog or disabling a valid version page link. Co-authored-by: deepseek-flash --- .../hmcl/ui/instances/AddonUpdatesPage.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index e999efbaf3..b72b189fa6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -41,6 +41,7 @@ import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; +import org.jackhuang.hmcl.util.FXThread; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.io.CSVTable; @@ -299,6 +300,12 @@ public void setSource(String source) { private static final class AddonChangelog extends JFXDialogLayout { private final Map changelogCache = new HashMap<>(); + /// ID of the most recently started changelog request. Only the result of this request may update the UI. + @FXThread + private int changelogRequestId = 0; + /// ID of the most recently started version page request. Only the result of this request may update the UI. + @FXThread + private int versionPageRequestId = 0; public AddonChangelog(AddonUpdateObject object) { List availableVersions = object.data.availableVersions(); @@ -364,6 +371,7 @@ public AddonChangelog(AddonUpdateObject object) { private void loadChangelog(AddonUpdateObject object, RemoteAddon.Version version, SpinnerPane spinnerPane, ScrollPane scrollPane) { if (version == null) return; + int requestId = ++changelogRequestId; spinnerPane.setLoading(true); RemoteAddonRepository repo = object.data.source().getRepoForType(object.data.repoType()); Task.supplyAsync(() -> { @@ -377,9 +385,10 @@ private void loadChangelog(AddonUpdateObject object, RemoteAddon.Version version "238222".equals(version.projectId()) ); }).whenComplete(Schedulers.javafx(), (result, exception) -> { - RemoteAddon.Version currentVersion = object.targetVersionObject.get(); - if (currentVersion != null && !currentVersion.versionId().equals(version.versionId())) { - // Version changed while loading, discard this result. Why's there no 'Task.interrupt()' + if (requestId != changelogRequestId) { + // A newer request has been started, discard this stale result + // NOTE: comparing version might not be enough: selecting A -> B -> A makes two + // requests for the same version, and the older one may finish last. return; } @@ -398,13 +407,13 @@ private void loadChangelog(AddonUpdateObject object, RemoteAddon.Version version private void loadVersionPageUrl(AddonUpdateObject object, JFXHyperlink button, RemoteAddon.Version version) { button.setDisable(true); + int requestId = ++versionPageRequestId; Task.supplyAsync(() -> { RemoteAddonRepository repo = object.data.source().getRepoForType(object.data.repoType()); return repo == null ? null : repo.getVersionPageUrl(version); }).whenComplete(Schedulers.javafx(), (result, exception) -> { - RemoteAddon.Version currentVersion = object.targetVersionObject.get(); - if (currentVersion != null && !currentVersion.versionId().equals(version.versionId())) { - // Version changed while loading, discard this result + if (requestId != versionPageRequestId) { + // A newer request has been started, discard this stale result return; } From b3d5a40120251dd726378d6bbbfe79b759de414c Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Thu, 10 Sep 2026 19:51:27 +0800 Subject: [PATCH 17/19] use ConcurrentHashMap for changelog cache it's being accessed from multiple thread --- .../java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index b72b189fa6..2083e7335a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -56,6 +56,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.stream.Collectors; @@ -299,7 +300,7 @@ public void setSource(String source) { } private static final class AddonChangelog extends JFXDialogLayout { - private final Map changelogCache = new HashMap<>(); + private final Map changelogCache = new ConcurrentHashMap<>(); /// ID of the most recently started changelog request. Only the result of this request may update the UI. @FXThread private int changelogRequestId = 0; From a41141ae57502d8ed5dcd633599f2fd175e89b3f Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Thu, 10 Sep 2026 19:54:09 +0800 Subject: [PATCH 18/19] fixup! fix: supersede stale changelog and version-page requests by request id --- .../java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java | 1 + 1 file changed, 1 insertion(+) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index 2083e7335a..29a2e07e9d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -372,6 +372,7 @@ public AddonChangelog(AddonUpdateObject object) { private void loadChangelog(AddonUpdateObject object, RemoteAddon.Version version, SpinnerPane spinnerPane, ScrollPane scrollPane) { if (version == null) return; + // will anyone actually change selected version 10000000+ times? int requestId = ++changelogRequestId; spinnerPane.setLoading(true); RemoteAddonRepository repo = object.data.source().getRepoForType(object.data.repoType()); From f2cc90f690af2c8302a169a9ba3c1ece794e6c04 Mon Sep 17 00:00:00 2001 From: ZZZank <47418975+ZZZank@users.noreply.github.com> Date: Thu, 10 Sep 2026 20:31:27 +0800 Subject: [PATCH 19/19] fix import --- .../java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java | 1 - 1 file changed, 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java index 29a2e07e9d..9af8eeaea9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/AddonUpdatesPage.java @@ -53,7 +53,6 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap;