mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
Add update check and change log
This commit is contained in:
@@ -13,6 +13,7 @@ from operator import itemgetter
|
|||||||
from ..services.websocket_manager import ws_manager
|
from ..services.websocket_manager import ws_manager
|
||||||
from ..services.settings_manager import settings
|
from ..services.settings_manager import settings
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from .update_routes import UpdateRoutes
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -43,6 +44,9 @@ class ApiRoutes:
|
|||||||
app.router.add_post('/loras/api/save-metadata', routes.save_metadata)
|
app.router.add_post('/loras/api/save-metadata', routes.save_metadata)
|
||||||
app.router.add_get('/api/lora-preview-url', routes.get_lora_preview_url) # Add new route
|
app.router.add_get('/api/lora-preview-url', routes.get_lora_preview_url) # Add new route
|
||||||
|
|
||||||
|
# Add update check routes
|
||||||
|
UpdateRoutes.setup_routes(app)
|
||||||
|
|
||||||
async def delete_model(self, request: web.Request) -> web.Response:
|
async def delete_model(self, request: web.Request) -> web.Response:
|
||||||
"""Handle model deletion request"""
|
"""Handle model deletion request"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
180
py/routes/update_routes.py
Normal file
180
py/routes/update_routes.py
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
import os
|
||||||
|
import aiohttp
|
||||||
|
import logging
|
||||||
|
import toml
|
||||||
|
from aiohttp import web
|
||||||
|
from typing import Dict, Any, List
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class UpdateRoutes:
|
||||||
|
"""Routes for handling plugin update checks"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def setup_routes(app):
|
||||||
|
"""Register update check routes"""
|
||||||
|
app.router.add_get('/loras/api/check-updates', UpdateRoutes.check_updates)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def check_updates(request):
|
||||||
|
"""
|
||||||
|
Check for plugin updates by comparing local version with GitHub
|
||||||
|
Returns update status and version information
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Read local version from pyproject.toml
|
||||||
|
local_version = UpdateRoutes._get_local_version()
|
||||||
|
logger.info(f"Local version: {local_version}")
|
||||||
|
|
||||||
|
# Fetch remote version from GitHub
|
||||||
|
remote_version, changelog = await UpdateRoutes._get_remote_version()
|
||||||
|
logger.info(f"Remote version: {remote_version}")
|
||||||
|
|
||||||
|
# Compare versions
|
||||||
|
update_available = UpdateRoutes._compare_versions(
|
||||||
|
local_version.replace('v', ''),
|
||||||
|
remote_version.replace('v', '')
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"Update available: {update_available}")
|
||||||
|
|
||||||
|
return web.json_response({
|
||||||
|
'success': True,
|
||||||
|
'current_version': local_version,
|
||||||
|
'latest_version': remote_version,
|
||||||
|
'update_available': update_available,
|
||||||
|
'changelog': changelog
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to check for updates: {e}", exc_info=True)
|
||||||
|
return web.json_response({
|
||||||
|
'success': False,
|
||||||
|
'error': str(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_local_version() -> str:
|
||||||
|
"""Get local plugin version from pyproject.toml"""
|
||||||
|
try:
|
||||||
|
# Find the plugin's pyproject.toml file
|
||||||
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
plugin_root = os.path.dirname(os.path.dirname(current_dir))
|
||||||
|
pyproject_path = os.path.join(plugin_root, 'pyproject.toml')
|
||||||
|
|
||||||
|
# Read and parse the toml file
|
||||||
|
if os.path.exists(pyproject_path):
|
||||||
|
with open(pyproject_path, 'r', encoding='utf-8') as f:
|
||||||
|
project_data = toml.load(f)
|
||||||
|
version = project_data.get('project', {}).get('version', '0.0.0')
|
||||||
|
return f"v{version}"
|
||||||
|
else:
|
||||||
|
logger.warning(f"pyproject.toml not found at {pyproject_path}")
|
||||||
|
return "v0.0.0"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to get local version: {e}", exc_info=True)
|
||||||
|
return "v0.0.0"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def _get_remote_version() -> tuple[str, List[str]]:
|
||||||
|
"""
|
||||||
|
Fetch remote version from GitHub
|
||||||
|
Returns:
|
||||||
|
tuple: (version string, changelog 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"
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(github_url, headers={'Accept': 'application/vnd.github+json'}) as response:
|
||||||
|
if response.status != 200:
|
||||||
|
logger.warning(f"Failed to fetch GitHub release: {response.status}")
|
||||||
|
return "v0.0.0", []
|
||||||
|
|
||||||
|
data = await response.json()
|
||||||
|
version = data.get('tag_name', '')
|
||||||
|
if not version.startswith('v'):
|
||||||
|
version = f"v{version}"
|
||||||
|
|
||||||
|
# Extract changelog from release notes
|
||||||
|
body = data.get('body', '')
|
||||||
|
changelog = UpdateRoutes._parse_changelog(body)
|
||||||
|
|
||||||
|
return version, changelog
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error fetching remote version: {e}", exc_info=True)
|
||||||
|
return "v0.0.0", []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _parse_changelog(release_notes: str) -> List[str]:
|
||||||
|
"""
|
||||||
|
Parse GitHub release notes to extract changelog items
|
||||||
|
|
||||||
|
Args:
|
||||||
|
release_notes: GitHub release notes markdown text
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of changelog items
|
||||||
|
"""
|
||||||
|
changelog = []
|
||||||
|
|
||||||
|
# Simple parsing - extract bullet points
|
||||||
|
lines = release_notes.split('\n')
|
||||||
|
for line in lines:
|
||||||
|
line = line.strip()
|
||||||
|
# Look for bullet points or numbered items
|
||||||
|
if line.startswith('- ') or line.startswith('* '):
|
||||||
|
item = line[2:].strip()
|
||||||
|
if item:
|
||||||
|
changelog.append(item)
|
||||||
|
# Match numbered items like "1. Item"
|
||||||
|
elif len(line) > 2 and line[0].isdigit() and line[1:].startswith('. '):
|
||||||
|
item = line[line.index('. ')+2:].strip()
|
||||||
|
if item:
|
||||||
|
changelog.append(item)
|
||||||
|
|
||||||
|
# If we couldn't parse specific items, use the whole text (limited)
|
||||||
|
if not changelog and release_notes:
|
||||||
|
# Limit to first 500 chars and add ellipsis
|
||||||
|
summary = release_notes.strip()[:500]
|
||||||
|
if len(release_notes) > 500:
|
||||||
|
summary += "..."
|
||||||
|
changelog.append(summary)
|
||||||
|
|
||||||
|
return changelog
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _compare_versions(version1: str, version2: str) -> bool:
|
||||||
|
"""
|
||||||
|
Compare two semantic version strings
|
||||||
|
Returns True if version2 is newer than version1
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Split versions into components
|
||||||
|
v1_parts = [int(x) for x in version1.split('.')]
|
||||||
|
v2_parts = [int(x) for x in version2.split('.')]
|
||||||
|
|
||||||
|
# Ensure both have 3 components (major.minor.patch)
|
||||||
|
while len(v1_parts) < 3:
|
||||||
|
v1_parts.append(0)
|
||||||
|
while len(v2_parts) < 3:
|
||||||
|
v2_parts.append(0)
|
||||||
|
|
||||||
|
# Compare version components
|
||||||
|
for i in range(3):
|
||||||
|
if v2_parts[i] > v1_parts[i]:
|
||||||
|
return True
|
||||||
|
elif v2_parts[i] < v1_parts[i]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Versions are equal
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error comparing versions: {e}", exc_info=True)
|
||||||
|
return False
|
||||||
@@ -1224,14 +1224,14 @@ body.modal-open {
|
|||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-section, .update-instructions {
|
.changelog-section {
|
||||||
background: var(--bg-color);
|
background: var(--bg-color);
|
||||||
border: 1px solid var(--lora-border);
|
border: 1px solid var(--lora-border);
|
||||||
border-radius: var(--border-radius-sm);
|
border-radius: var(--border-radius-sm);
|
||||||
padding: var(--space-2);
|
padding: var(--space-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.changelog-section h3, .update-instructions h3 {
|
.changelog-section h3 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: var(--space-2);
|
margin-bottom: var(--space-2);
|
||||||
color: var(--lora-accent);
|
color: var(--lora-accent);
|
||||||
@@ -1239,7 +1239,7 @@ body.modal-open {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.changelog-content {
|
.changelog-content {
|
||||||
max-height: 200px;
|
max-height: 300px; /* Increased height since we removed instructions */
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1272,17 +1272,6 @@ body.modal-open {
|
|||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.code-block {
|
|
||||||
background: var(--lora-surface);
|
|
||||||
border: 1px solid var(--lora-border);
|
|
||||||
border-radius: var(--border-radius-xs);
|
|
||||||
padding: var(--space-1) var(--space-2);
|
|
||||||
margin: var(--space-1) 0;
|
|
||||||
font-family: monospace;
|
|
||||||
color: var(--text-color);
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.update-info {
|
.update-info {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -1353,10 +1342,4 @@ input:checked + .toggle-slider:before {
|
|||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.preference-note {
|
/* ...existing code... */
|
||||||
margin-top: 4px;
|
|
||||||
font-size: 0.8em;
|
|
||||||
color: var(--text-color);
|
|
||||||
opacity: 0.7;
|
|
||||||
margin-left: 52px;
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { debounce } from './utils/debounce.js';
|
import { debounce } from './utils/debounce.js';
|
||||||
import { LoadingManager } from './managers/LoadingManager.js';
|
import { LoadingManager } from './managers/LoadingManager.js';
|
||||||
import { modalManager } from './managers/ModalManager.js';
|
import { modalManager } from './managers/ModalManager.js';
|
||||||
|
import { updateService } from './managers/UpdateService.js';
|
||||||
import { state } from './state/index.js';
|
import { state } from './state/index.js';
|
||||||
import { showLoraModal, toggleShowcase, scrollToTop } from './components/LoraCard.js';
|
import { showLoraModal, toggleShowcase, scrollToTop } from './components/LoraCard.js';
|
||||||
import { loadMoreLoras, fetchCivitai, deleteModel, replacePreview, resetAndReload, refreshLoras } from './api/loraApi.js';
|
import { loadMoreLoras, fetchCivitai, deleteModel, replacePreview, resetAndReload, refreshLoras } from './api/loraApi.js';
|
||||||
@@ -50,9 +51,10 @@ window.toggleShowcase = toggleShowcase;
|
|||||||
window.scrollToTop = scrollToTop;
|
window.scrollToTop = scrollToTop;
|
||||||
|
|
||||||
// Initialize everything when DOM is ready
|
// Initialize everything when DOM is ready
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
state.loadingManager = new LoadingManager();
|
state.loadingManager = new LoadingManager();
|
||||||
modalManager.initialize(); // Initialize modalManager after DOM is loaded
|
modalManager.initialize(); // Initialize modalManager after DOM is loaded
|
||||||
|
updateService.initialize(); // Initialize updateService after modalManager
|
||||||
window.downloadManager = new DownloadManager(); // Move this after modalManager initialization
|
window.downloadManager = new DownloadManager(); // Move this after modalManager initialization
|
||||||
window.filterManager = new FilterManager(); // Initialize filter manager
|
window.filterManager = new FilterManager(); // Initialize filter manager
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ export class ModalManager {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.modals = new Map();
|
this.modals = new Map();
|
||||||
this.scrollPosition = 0;
|
this.scrollPosition = 0;
|
||||||
this.updateAvailable = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
@@ -75,9 +74,8 @@ export class ModalManager {
|
|||||||
document.addEventListener('keydown', this.boundHandleEscape);
|
document.addEventListener('keydown', this.boundHandleEscape);
|
||||||
this.initialized = true;
|
this.initialized = true;
|
||||||
|
|
||||||
// Initialize corner controls and update modal
|
// Initialize corner controls
|
||||||
this.initCornerControls();
|
this.initCornerControls();
|
||||||
this.initUpdateModal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerModal(id, config) {
|
registerModal(id, config) {
|
||||||
@@ -159,79 +157,16 @@ export class ModalManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add method to initialize corner controls behavior
|
// Keep only the corner controls initialization
|
||||||
initCornerControls() {
|
initCornerControls() {
|
||||||
const cornerControls = document.querySelector('.corner-controls');
|
const cornerControls = document.querySelector('.corner-controls');
|
||||||
const cornerControlsToggle = document.querySelector('.corner-controls-toggle');
|
const cornerControlsToggle = document.querySelector('.corner-controls-toggle');
|
||||||
|
|
||||||
if(cornerControls && cornerControlsToggle) {
|
if(cornerControls && cornerControlsToggle) {
|
||||||
// Check for updates (mock implementation)
|
// Toggle corner controls visibility
|
||||||
this.checkForUpdates();
|
cornerControlsToggle.addEventListener('click', () => {
|
||||||
|
cornerControls.classList.toggle('expanded');
|
||||||
// Apply the initial badge state based on localStorage
|
});
|
||||||
const showUpdates = localStorage.getItem('show_update_notifications');
|
|
||||||
if (showUpdates === 'false') {
|
|
||||||
this.updateBadgeVisibility(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modified update checker
|
|
||||||
checkForUpdates() {
|
|
||||||
// First check if user has disabled update notifications
|
|
||||||
const showUpdates = localStorage.getItem('show_update_notifications');
|
|
||||||
|
|
||||||
// For demo purposes, we'll simulate an update being available
|
|
||||||
setTimeout(() => {
|
|
||||||
// We have an update available (mock)
|
|
||||||
this.updateAvailable = true;
|
|
||||||
|
|
||||||
// Only show badges if notifications are enabled
|
|
||||||
const shouldShow = showUpdates !== 'false';
|
|
||||||
this.updateBadgeVisibility(shouldShow);
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add method to initialize update modal
|
|
||||||
initUpdateModal() {
|
|
||||||
const updateModal = document.getElementById('updateModal');
|
|
||||||
if (!updateModal) return;
|
|
||||||
|
|
||||||
const checkbox = updateModal.querySelector('#updateNotifications');
|
|
||||||
if (!checkbox) return;
|
|
||||||
|
|
||||||
// Set initial state from localStorage or default to true
|
|
||||||
const showUpdates = localStorage.getItem('show_update_notifications');
|
|
||||||
checkbox.checked = showUpdates === null || showUpdates === 'true';
|
|
||||||
|
|
||||||
// Apply the initial badge visibility based on checkbox state
|
|
||||||
this.updateBadgeVisibility(checkbox.checked);
|
|
||||||
|
|
||||||
// Add event listener for changes
|
|
||||||
checkbox.addEventListener('change', (e) => {
|
|
||||||
localStorage.setItem('show_update_notifications', e.target.checked);
|
|
||||||
|
|
||||||
// Immediately update badge visibility based on the new setting
|
|
||||||
this.updateBadgeVisibility(e.target.checked);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enhanced helper method to update badge visibility
|
|
||||||
updateBadgeVisibility(show) {
|
|
||||||
const updateToggle = document.querySelector('.update-toggle');
|
|
||||||
const updateBadge = document.querySelector('.update-toggle .update-badge');
|
|
||||||
const cornerBadge = document.querySelector('.corner-badge');
|
|
||||||
|
|
||||||
if (updateToggle) {
|
|
||||||
updateToggle.title = show && this.updateAvailable ? "Update Available" : "Check Updates";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updateBadge) {
|
|
||||||
updateBadge.classList.toggle('hidden', !(show && this.updateAvailable));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cornerBadge) {
|
|
||||||
cornerBadge.classList.toggle('hidden', !(show && this.updateAvailable));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
183
static/js/managers/UpdateService.js
Normal file
183
static/js/managers/UpdateService.js
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
import { modalManager } from './ModalManager.js';
|
||||||
|
|
||||||
|
export class UpdateService {
|
||||||
|
constructor() {
|
||||||
|
this.updateCheckInterval = 24 * 60 * 60 * 1000; // 24 hours
|
||||||
|
this.currentVersion = "v0.0.0"; // Initialize with default values
|
||||||
|
this.latestVersion = "v0.0.0"; // Initialize with default values
|
||||||
|
this.updateInfo = null;
|
||||||
|
this.updateAvailable = false;
|
||||||
|
this.updateNotificationsEnabled = localStorage.getItem('show_update_notifications') !== 'false';
|
||||||
|
this.lastCheckTime = parseInt(localStorage.getItem('last_update_check') || '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
// Initialize update preferences from localStorage
|
||||||
|
const showUpdates = localStorage.getItem('show_update_notifications');
|
||||||
|
this.updateNotificationsEnabled = showUpdates === null || showUpdates === 'true';
|
||||||
|
|
||||||
|
// Register event listener for update notification toggle
|
||||||
|
const updateCheckbox = document.getElementById('updateNotifications');
|
||||||
|
if (updateCheckbox) {
|
||||||
|
updateCheckbox.checked = this.updateNotificationsEnabled;
|
||||||
|
updateCheckbox.addEventListener('change', (e) => {
|
||||||
|
this.updateNotificationsEnabled = e.target.checked;
|
||||||
|
localStorage.setItem('show_update_notifications', e.target.checked);
|
||||||
|
this.updateBadgeVisibility();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform update check if needed
|
||||||
|
this.checkForUpdates();
|
||||||
|
|
||||||
|
// Set up event listener for update button
|
||||||
|
const updateToggle = document.querySelector('.update-toggle');
|
||||||
|
if (updateToggle) {
|
||||||
|
updateToggle.addEventListener('click', () => this.showUpdateModal());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Immediately update modal content with current values (even if from default)
|
||||||
|
this.updateModalContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkForUpdates() {
|
||||||
|
// Check if we should perform an update check
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - this.lastCheckTime < this.updateCheckInterval) {
|
||||||
|
// If we already have update info, just update the UI
|
||||||
|
if (this.updateAvailable) {
|
||||||
|
this.updateBadgeVisibility();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Call backend API to check for updates
|
||||||
|
const response = await fetch('/loras/api/check-updates');
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
this.currentVersion = data.current_version || "v0.0.0";
|
||||||
|
this.latestVersion = data.latest_version || "v0.0.0";
|
||||||
|
this.updateInfo = data;
|
||||||
|
|
||||||
|
// Determine if update is available
|
||||||
|
this.updateAvailable = data.update_available;
|
||||||
|
|
||||||
|
// Update last check time
|
||||||
|
this.lastCheckTime = now;
|
||||||
|
localStorage.setItem('last_update_check', now.toString());
|
||||||
|
|
||||||
|
// Update UI
|
||||||
|
this.updateBadgeVisibility();
|
||||||
|
this.updateModalContent();
|
||||||
|
|
||||||
|
console.log("Update check complete:", {
|
||||||
|
currentVersion: this.currentVersion,
|
||||||
|
latestVersion: this.latestVersion,
|
||||||
|
updateAvailable: this.updateAvailable
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to check for updates:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBadgeVisibility() {
|
||||||
|
const updateToggle = document.querySelector('.update-toggle');
|
||||||
|
const updateBadge = document.querySelector('.update-toggle .update-badge');
|
||||||
|
const cornerBadge = document.querySelector('.corner-badge');
|
||||||
|
|
||||||
|
if (updateToggle) {
|
||||||
|
updateToggle.title = this.updateNotificationsEnabled && this.updateAvailable
|
||||||
|
? "Update Available"
|
||||||
|
: "Check Updates";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateBadge) {
|
||||||
|
const shouldShow = this.updateNotificationsEnabled && this.updateAvailable;
|
||||||
|
updateBadge.classList.toggle('hidden', !shouldShow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cornerBadge) {
|
||||||
|
const shouldShow = this.updateNotificationsEnabled && this.updateAvailable;
|
||||||
|
cornerBadge.classList.toggle('hidden', !shouldShow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateModalContent() {
|
||||||
|
const modal = document.getElementById('updateModal');
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
// Update title based on update availability
|
||||||
|
const headerTitle = modal.querySelector('.update-header h2');
|
||||||
|
if (headerTitle) {
|
||||||
|
headerTitle.textContent = this.updateAvailable ? "Update Available" : "Check for Updates";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always update version information, even if updateInfo is null
|
||||||
|
const currentVersionEl = modal.querySelector('.current-version .version-number');
|
||||||
|
const newVersionEl = modal.querySelector('.new-version .version-number');
|
||||||
|
|
||||||
|
if (currentVersionEl) currentVersionEl.textContent = this.currentVersion;
|
||||||
|
if (newVersionEl) newVersionEl.textContent = this.latestVersion;
|
||||||
|
|
||||||
|
// Update changelog content if available
|
||||||
|
if (this.updateInfo && this.updateInfo.changelog) {
|
||||||
|
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 = `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');
|
||||||
|
listItem.textContent = item;
|
||||||
|
changelogList.appendChild(listItem);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If no changelog items available
|
||||||
|
const listItem = document.createElement('li');
|
||||||
|
listItem.textContent = "No detailed changelog available. Check GitHub for more information.";
|
||||||
|
changelogList.appendChild(listItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
changelogItem.appendChild(changelogList);
|
||||||
|
changelogContent.appendChild(changelogItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update GitHub link to point to the specific release if available
|
||||||
|
const githubLink = modal.querySelector('.update-link');
|
||||||
|
if (githubLink && this.latestVersion) {
|
||||||
|
const versionTag = this.latestVersion.replace(/^v/, '');
|
||||||
|
githubLink.href = `https://github.com/willmiao/ComfyUI-Lora-Manager/releases/tag/v${versionTag}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showUpdateModal() {
|
||||||
|
// Force a check for updates when showing the modal
|
||||||
|
this.manualCheckForUpdates().then(() => {
|
||||||
|
// Show the modal after update check completes
|
||||||
|
modalManager.showModal('updateModal');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async manualCheckForUpdates() {
|
||||||
|
this.lastCheckTime = 0; // Reset last check time to force check
|
||||||
|
await this.checkForUpdates();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and export singleton instance
|
||||||
|
export const updateService = new UpdateService();
|
||||||
@@ -223,18 +223,18 @@
|
|||||||
<button class="close" onclick="modalManager.closeModal('updateModal')">×</button>
|
<button class="close" onclick="modalManager.closeModal('updateModal')">×</button>
|
||||||
<div class="update-header">
|
<div class="update-header">
|
||||||
<i class="fas fa-bell update-icon"></i>
|
<i class="fas fa-bell update-icon"></i>
|
||||||
<h2>Update Available</h2>
|
<h2>Check for Updates</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="update-content">
|
<div class="update-content">
|
||||||
<div class="update-info">
|
<div class="update-info">
|
||||||
<div class="version-info">
|
<div class="version-info">
|
||||||
<div class="current-version">
|
<div class="current-version">
|
||||||
<span class="label">Current Version:</span>
|
<span class="label">Current Version:</span>
|
||||||
<span class="version-number">v1.0.0</span>
|
<span class="version-number">v0.0.0</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="new-version">
|
<div class="new-version">
|
||||||
<span class="label">New Version:</span>
|
<span class="label">New Version:</span>
|
||||||
<span class="version-number">v1.1.0</span>
|
<span class="version-number">v0.0.0</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="https://github.com/willmiao/ComfyUI-Lora-Manager" target="_blank" class="update-link">
|
<a href="https://github.com/willmiao/ComfyUI-Lora-Manager" target="_blank" class="update-link">
|
||||||
@@ -245,27 +245,14 @@
|
|||||||
<div class="changelog-section">
|
<div class="changelog-section">
|
||||||
<h3>Changelog</h3>
|
<h3>Changelog</h3>
|
||||||
<div class="changelog-content">
|
<div class="changelog-content">
|
||||||
|
<!-- Dynamic changelog content will be inserted here -->
|
||||||
<div class="changelog-item">
|
<div class="changelog-item">
|
||||||
<h4>Version 1.1.0</h4>
|
<h4>Checking for updates...</h4>
|
||||||
<ul>
|
<p>Please wait while we check for the latest version.</p>
|
||||||
<li>Added support for custom folder structures</li>
|
|
||||||
<li>Improved search functionality</li>
|
|
||||||
<li>Fixed bug with loading large libraries</li>
|
|
||||||
<li>Added new sorting options</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="update-instructions">
|
|
||||||
<h3>How to Update</h3>
|
|
||||||
<p>To update LoRA Manager, run the following command in your ComfyUI directory:</p>
|
|
||||||
<div class="code-block">
|
|
||||||
<code>git pull origin main</code>
|
|
||||||
</div>
|
|
||||||
<p>Or update through the ComfyUI Manager if you installed it that way.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="update-preferences">
|
<div class="update-preferences">
|
||||||
<label class="toggle-switch">
|
<label class="toggle-switch">
|
||||||
<input type="checkbox" id="updateNotifications" checked>
|
<input type="checkbox" id="updateNotifications" checked>
|
||||||
|
|||||||
Reference in New Issue
Block a user