mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat(update-modal): display last 5 release notes instead of single
- Modified backend to fetch last 5 releases from GitHub API - Updated frontend to iterate through and display multiple releases - Added latest badge and publish date styling - Added update.latestBadge translation key to all locales - Maintains backward compatibility for single changelog display
This commit is contained in:
@@ -1214,6 +1214,7 @@
|
||||
"checkingUpdates": "Nach Updates wird gesucht...",
|
||||
"checkingMessage": "Bitte warten Sie, während wir nach der neuesten Version suchen.",
|
||||
"showNotifications": "Update-Benachrichtigungen anzeigen",
|
||||
"latestBadge": "Neueste",
|
||||
"updateProgress": {
|
||||
"preparing": "Update wird vorbereitet...",
|
||||
"installing": "Update wird installiert...",
|
||||
|
||||
@@ -1214,6 +1214,7 @@
|
||||
"checkingUpdates": "Checking for updates...",
|
||||
"checkingMessage": "Please wait while we check for the latest version.",
|
||||
"showNotifications": "Show update notifications",
|
||||
"latestBadge": "Latest",
|
||||
"updateProgress": {
|
||||
"preparing": "Preparing update...",
|
||||
"installing": "Installing update...",
|
||||
|
||||
@@ -1214,6 +1214,7 @@
|
||||
"checkingUpdates": "Comprobando actualizaciones...",
|
||||
"checkingMessage": "Por favor espera mientras comprobamos la última versión.",
|
||||
"showNotifications": "Mostrar notificaciones de actualización",
|
||||
"latestBadge": "Último",
|
||||
"updateProgress": {
|
||||
"preparing": "Preparando actualización...",
|
||||
"installing": "Instalando actualización...",
|
||||
|
||||
@@ -1214,6 +1214,7 @@
|
||||
"checkingUpdates": "Vérification des mises à jour...",
|
||||
"checkingMessage": "Veuillez patienter pendant la vérification de la dernière version.",
|
||||
"showNotifications": "Afficher les notifications de mise à jour",
|
||||
"latestBadge": "Dernier",
|
||||
"updateProgress": {
|
||||
"preparing": "Préparation de la mise à jour...",
|
||||
"installing": "Installation de la mise à jour...",
|
||||
|
||||
@@ -1214,6 +1214,7 @@
|
||||
"checkingUpdates": "בודק עדכונים...",
|
||||
"checkingMessage": "אנא המתן בזמן שאנו בודקים את הגרסה האחרונה.",
|
||||
"showNotifications": "הצג התראות עדכון",
|
||||
"latestBadge": "עדכן",
|
||||
"updateProgress": {
|
||||
"preparing": "מכין עדכון...",
|
||||
"installing": "מתקין עדכון...",
|
||||
|
||||
@@ -1214,6 +1214,7 @@
|
||||
"checkingUpdates": "更新を確認中...",
|
||||
"checkingMessage": "最新バージョンを確認しています。お待ちください。",
|
||||
"showNotifications": "更新通知を表示",
|
||||
"latestBadge": "最新",
|
||||
"updateProgress": {
|
||||
"preparing": "更新を準備中...",
|
||||
"installing": "更新をインストール中...",
|
||||
|
||||
@@ -1214,6 +1214,7 @@
|
||||
"checkingUpdates": "업데이트 확인 중...",
|
||||
"checkingMessage": "최신 버전을 확인하는 동안 잠시 기다려주세요.",
|
||||
"showNotifications": "업데이트 알림 표시",
|
||||
"latestBadge": "최신",
|
||||
"updateProgress": {
|
||||
"preparing": "업데이트 준비 중...",
|
||||
"installing": "업데이트 설치 중...",
|
||||
|
||||
@@ -1214,6 +1214,7 @@
|
||||
"checkingUpdates": "Проверка обновлений...",
|
||||
"checkingMessage": "Пожалуйста, подождите, пока мы проверяем последнюю версию.",
|
||||
"showNotifications": "Показывать уведомления об обновлениях",
|
||||
"latestBadge": "Последний",
|
||||
"updateProgress": {
|
||||
"preparing": "Подготовка обновления...",
|
||||
"installing": "Установка обновления...",
|
||||
|
||||
@@ -1214,6 +1214,7 @@
|
||||
"checkingUpdates": "正在检查更新...",
|
||||
"checkingMessage": "请稍候,正在检查最新版本。",
|
||||
"showNotifications": "显示更新通知",
|
||||
"latestBadge": "最新",
|
||||
"updateProgress": {
|
||||
"preparing": "正在准备更新...",
|
||||
"installing": "正在安装更新...",
|
||||
@@ -1538,4 +1539,4 @@
|
||||
"learnMore": "浏览器插件教程"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1214,6 +1214,7 @@
|
||||
"checkingUpdates": "正在檢查更新...",
|
||||
"checkingMessage": "請稍候,正在檢查最新版本。",
|
||||
"showNotifications": "顯示更新通知",
|
||||
"latestBadge": "最新",
|
||||
"updateProgress": {
|
||||
"preparing": "正在準備更新...",
|
||||
"installing": "正在安裝更新...",
|
||||
|
||||
@@ -45,8 +45,9 @@ class UpdateRoutes:
|
||||
# Fetch remote version from GitHub
|
||||
if nightly:
|
||||
remote_version, changelog = await UpdateRoutes._get_nightly_version()
|
||||
releases = None
|
||||
else:
|
||||
remote_version, changelog = await UpdateRoutes._get_remote_version()
|
||||
remote_version, changelog, releases = await UpdateRoutes._get_remote_version()
|
||||
|
||||
# Compare versions
|
||||
if nightly:
|
||||
@@ -59,7 +60,7 @@ class UpdateRoutes:
|
||||
remote_version.replace('v', '')
|
||||
)
|
||||
|
||||
return web.json_response({
|
||||
response_data = {
|
||||
'success': True,
|
||||
'current_version': local_version,
|
||||
'latest_version': remote_version,
|
||||
@@ -67,7 +68,13 @@ class UpdateRoutes:
|
||||
'changelog': changelog,
|
||||
'git_info': git_info,
|
||||
'nightly': nightly
|
||||
})
|
||||
}
|
||||
|
||||
# Include releases list for stable mode
|
||||
if releases is not None:
|
||||
response_data['releases'] = releases
|
||||
|
||||
return web.json_response(response_data)
|
||||
|
||||
except NETWORK_EXCEPTIONS as e:
|
||||
logger.warning("Network unavailable during update check: %s", e)
|
||||
@@ -443,42 +450,58 @@ class UpdateRoutes:
|
||||
return git_info
|
||||
|
||||
@staticmethod
|
||||
async def _get_remote_version() -> tuple[str, List[str]]:
|
||||
async def _get_remote_version() -> tuple[str, List[str], List[Dict]]:
|
||||
"""
|
||||
Fetch remote version from GitHub
|
||||
Returns:
|
||||
tuple: (version string, changelog list)
|
||||
tuple: (version string, changelog list, releases list)
|
||||
"""
|
||||
repo_owner = "willmiao"
|
||||
repo_name = "ComfyUI-Lora-Manager"
|
||||
|
||||
# Use GitHub API to fetch the latest release
|
||||
github_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/releases/latest"
|
||||
# Use GitHub API to fetch the last 5 releases
|
||||
github_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/releases?per_page=5"
|
||||
|
||||
try:
|
||||
downloader = await get_downloader()
|
||||
success, data = await downloader.make_request('GET', github_url, custom_headers={'Accept': 'application/vnd.github+json'})
|
||||
|
||||
if not success:
|
||||
logger.warning(f"Failed to fetch GitHub release: {data}")
|
||||
return "v0.0.0", []
|
||||
logger.warning(f"Failed to fetch GitHub releases: {data}")
|
||||
return "v0.0.0", [], []
|
||||
|
||||
version = data.get('tag_name', '')
|
||||
if not version.startswith('v'):
|
||||
version = f"v{version}"
|
||||
# Parse releases
|
||||
releases = []
|
||||
for i, release in enumerate(data):
|
||||
version = release.get('tag_name', '')
|
||||
if not version.startswith('v'):
|
||||
version = f"v{version}"
|
||||
|
||||
# Extract changelog from release notes
|
||||
body = release.get('body', '')
|
||||
changelog = UpdateRoutes._parse_changelog(body)
|
||||
|
||||
releases.append({
|
||||
'version': version,
|
||||
'changelog': changelog,
|
||||
'published_at': release.get('published_at', ''),
|
||||
'is_latest': i == 0
|
||||
})
|
||||
|
||||
# Extract changelog from release notes
|
||||
body = data.get('body', '')
|
||||
changelog = UpdateRoutes._parse_changelog(body)
|
||||
# Get latest version and its changelog
|
||||
if releases:
|
||||
latest_version = releases[0]['version']
|
||||
latest_changelog = releases[0]['changelog']
|
||||
return latest_version, latest_changelog, releases
|
||||
|
||||
return version, changelog
|
||||
return "v0.0.0", [], []
|
||||
|
||||
except NETWORK_EXCEPTIONS as e:
|
||||
logger.warning("Unable to reach GitHub for release info: %s", e)
|
||||
return "v0.0.0", []
|
||||
return "v0.0.0", [], []
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching remote version: {e}", exc_info=True)
|
||||
return "v0.0.0", []
|
||||
return "v0.0.0", [], []
|
||||
|
||||
@staticmethod
|
||||
def _parse_changelog(release_notes: str) -> List[str]:
|
||||
|
||||
@@ -121,4 +121,49 @@
|
||||
|
||||
.changelog-item a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
/* Multiple releases styling */
|
||||
.changelog-content > .changelog-item + .changelog-item {
|
||||
margin-top: var(--space-4);
|
||||
padding-top: var(--space-4);
|
||||
border-top: 1px solid var(--lora-border);
|
||||
}
|
||||
|
||||
.changelog-item h4 {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
margin-bottom: var(--space-2);
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.changelog-item .latest-badge {
|
||||
background-color: var(--lora-accent);
|
||||
color: white;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.75em;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.changelog-item .publish-date {
|
||||
font-size: 0.85em;
|
||||
color: var(--text-color);
|
||||
opacity: 0.6;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.changelog-item.latest {
|
||||
background-color: rgba(66, 153, 225, 0.05);
|
||||
border-radius: var(--border-radius-sm);
|
||||
padding: var(--space-3);
|
||||
border: 1px solid rgba(66, 153, 225, 0.2);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .changelog-item.latest {
|
||||
background-color: rgba(66, 153, 225, 0.1);
|
||||
border-color: rgba(66, 153, 225, 0.3);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
/* Update Modal Styles */
|
||||
.update-modal {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.update-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -509,38 +509,90 @@ export class UpdateService {
|
||||
}
|
||||
|
||||
// Update changelog content if available
|
||||
if (this.updateInfo && this.updateInfo.changelog) {
|
||||
if (this.updateInfo && (this.updateInfo.changelog || this.updateInfo.releases)) {
|
||||
const changelogContent = modal.querySelector('.changelog-content');
|
||||
if (changelogContent) {
|
||||
changelogContent.innerHTML = ''; // Clear existing content
|
||||
|
||||
// Create changelog item
|
||||
const changelogItem = document.createElement('div');
|
||||
changelogItem.className = 'changelog-item';
|
||||
|
||||
const versionHeader = document.createElement('h4');
|
||||
versionHeader.textContent = `${translate('common.status.version', {}, 'Version')} ${this.latestVersion}`;
|
||||
changelogItem.appendChild(versionHeader);
|
||||
|
||||
// Create changelog list
|
||||
const changelogList = document.createElement('ul');
|
||||
|
||||
if (this.updateInfo.changelog && this.updateInfo.changelog.length > 0) {
|
||||
this.updateInfo.changelog.forEach(item => {
|
||||
const listItem = document.createElement('li');
|
||||
// Parse markdown in changelog items
|
||||
listItem.innerHTML = this.parseMarkdown(item);
|
||||
changelogList.appendChild(listItem);
|
||||
// Check if we have multiple releases
|
||||
const releases = this.updateInfo.releases;
|
||||
if (releases && Array.isArray(releases) && releases.length > 0) {
|
||||
// Display multiple releases (up to 5)
|
||||
releases.forEach(release => {
|
||||
const changelogItem = document.createElement('div');
|
||||
changelogItem.className = 'changelog-item';
|
||||
if (release.is_latest) {
|
||||
changelogItem.classList.add('latest');
|
||||
}
|
||||
|
||||
const versionHeader = document.createElement('h4');
|
||||
|
||||
if (release.is_latest) {
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'latest-badge';
|
||||
badge.textContent = translate('update.latestBadge', {}, 'Latest');
|
||||
versionHeader.appendChild(badge);
|
||||
versionHeader.appendChild(document.createTextNode(' '));
|
||||
}
|
||||
|
||||
const versionSpan = document.createElement('span');
|
||||
versionSpan.className = 'version';
|
||||
versionSpan.textContent = `${translate('common.status.version', {}, 'Version')} ${release.version}`;
|
||||
versionHeader.appendChild(versionSpan);
|
||||
|
||||
if (release.published_at) {
|
||||
const dateSpan = document.createElement('span');
|
||||
dateSpan.className = 'publish-date';
|
||||
dateSpan.textContent = this.formatRelativeTime(new Date(release.published_at).getTime());
|
||||
versionHeader.appendChild(dateSpan);
|
||||
}
|
||||
|
||||
changelogItem.appendChild(versionHeader);
|
||||
|
||||
// Create changelog list
|
||||
const changelogList = document.createElement('ul');
|
||||
|
||||
if (release.changelog && release.changelog.length > 0) {
|
||||
release.changelog.forEach(item => {
|
||||
const listItem = document.createElement('li');
|
||||
listItem.innerHTML = this.parseMarkdown(item);
|
||||
changelogList.appendChild(listItem);
|
||||
});
|
||||
} else {
|
||||
const listItem = document.createElement('li');
|
||||
listItem.textContent = translate('update.noChangelogAvailable', {}, 'No detailed changelog available.');
|
||||
changelogList.appendChild(listItem);
|
||||
}
|
||||
|
||||
changelogItem.appendChild(changelogList);
|
||||
changelogContent.appendChild(changelogItem);
|
||||
});
|
||||
} else {
|
||||
// If no changelog items available
|
||||
const listItem = document.createElement('li');
|
||||
listItem.textContent = translate('update.noChangelogAvailable', {}, 'No detailed changelog available. Check GitHub for more information.');
|
||||
changelogList.appendChild(listItem);
|
||||
// Fallback: display single changelog (old behavior)
|
||||
const changelogItem = document.createElement('div');
|
||||
changelogItem.className = 'changelog-item';
|
||||
|
||||
const versionHeader = document.createElement('h4');
|
||||
versionHeader.textContent = `${translate('common.status.version', {}, 'Version')} ${this.latestVersion}`;
|
||||
changelogItem.appendChild(versionHeader);
|
||||
|
||||
const changelogList = document.createElement('ul');
|
||||
|
||||
if (this.updateInfo.changelog && this.updateInfo.changelog.length > 0) {
|
||||
this.updateInfo.changelog.forEach(item => {
|
||||
const listItem = document.createElement('li');
|
||||
listItem.innerHTML = this.parseMarkdown(item);
|
||||
changelogList.appendChild(listItem);
|
||||
});
|
||||
} else {
|
||||
const listItem = document.createElement('li');
|
||||
listItem.textContent = translate('update.noChangelogAvailable', {}, 'No detailed changelog available. Check GitHub for more information.');
|
||||
changelogList.appendChild(listItem);
|
||||
}
|
||||
|
||||
changelogItem.appendChild(changelogList);
|
||||
changelogContent.appendChild(changelogItem);
|
||||
}
|
||||
|
||||
changelogItem.appendChild(changelogList);
|
||||
changelogContent.appendChild(changelogItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,11 +24,12 @@ async def test_get_remote_version_offline_logs_without_traceback(monkeypatch, ca
|
||||
caplog.set_level(logging.WARNING)
|
||||
monkeypatch.setattr(update_routes, "get_downloader", lambda: _stub_downloader(OfflineDownloader()))
|
||||
|
||||
version, changelog = await update_routes.UpdateRoutes._get_remote_version()
|
||||
version, changelog, releases = await update_routes.UpdateRoutes._get_remote_version()
|
||||
|
||||
assert version == "v0.0.0"
|
||||
assert changelog == []
|
||||
assert "Failed to fetch GitHub release" in caplog.text
|
||||
assert releases == []
|
||||
assert "Failed to fetch GitHub releases" in caplog.text
|
||||
assert "Cannot connect to host" in caplog.text
|
||||
assert "Traceback" not in caplog.text
|
||||
|
||||
@@ -38,10 +39,11 @@ async def test_get_remote_version_network_error_logs_warning(monkeypatch, caplog
|
||||
caplog.set_level(logging.WARNING)
|
||||
monkeypatch.setattr(update_routes, "get_downloader", lambda: _stub_downloader(RaisingDownloader()))
|
||||
|
||||
version, changelog = await update_routes.UpdateRoutes._get_remote_version()
|
||||
version, changelog, releases = await update_routes.UpdateRoutes._get_remote_version()
|
||||
|
||||
assert version == "v0.0.0"
|
||||
assert changelog == []
|
||||
assert releases == []
|
||||
assert "Unable to reach GitHub for release info" in caplog.text
|
||||
assert "Traceback" not in caplog.text
|
||||
|
||||
|
||||
Reference in New Issue
Block a user