diff --git a/css/settings.css b/css/settings.css index 73959e4..f98942b 100644 --- a/css/settings.css +++ b/css/settings.css @@ -45,12 +45,10 @@ body { /* Server Status Display */ .server-status-container { border-top: 1px solid #e0e0e0; - padding: 8px 20px; background: #f9f9f9; margin-top: auto; display: flex; - align-items: center; - height: 55px; + flex-direction: column; } .server-status-content { @@ -58,7 +56,7 @@ body { align-items: center; justify-content: flex-start; width: 100%; - height: 100%; + padding: 8px 20px; } .server-status-text { @@ -1664,4 +1662,105 @@ textarea.form-input { border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto; +} + +/* CLI Version and Update Styles */ +.cli-version-container { + border-top: 1px solid #e0e0e0; + padding: 12px 20px; + background: #ffffff; + display: none; + flex-direction: column; + gap: 10px; +} + +.cli-version-container.show { + display: flex; +} + +.cli-version-info { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.cli-version-label { + font-size: 11px; + font-weight: 500; + color: #6b7280; +} + +.cli-version-text { + font-size: 11px; + font-weight: 600; + color: #000000; + font-family: 'SF Mono', 'Monaco', 'Consolas', monospace; +} + +.update-cli-btn { + width: 100%; + padding: 8px 12px; + background: #8b5cf6; + color: #ffffff; + border: none; + border-radius: 6px; + font-size: 12px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + font-family: inherit; +} + +.update-cli-btn:hover { + background: #7c3aed; +} + +.update-cli-btn:active { + transform: translateY(1px); +} + +.update-cli-btn:disabled { + background: #d1d5db; + cursor: not-allowed; +} + +/* Update Dialog Styles */ +.cli-update-details { + margin-top: 8px; + font-size: 13px; + color: #4b5563; + line-height: 1.6; +} + +/* Progress Bar Styles for Update Dialog */ +#cli-progress-dialog .progress-label { + font-size: 13px; + font-weight: 500; + margin-bottom: 12px; + color: #000000; + text-align: center; +} + +#cli-progress-dialog .progress-bar { + width: 100%; + height: 8px; + background: #e0e0e0; + border-radius: 4px; + overflow: hidden; + margin-bottom: 8px; +} + +#cli-progress-dialog .progress-fill { + height: 100%; + background: #8b5cf6; + border-radius: 4px; + transition: width 0.3s ease; + width: 0%; +} + +#cli-progress-dialog .progress-text { + font-size: 11px; + color: #6b7280; + text-align: center; } \ No newline at end of file diff --git a/js/settings-update.js b/js/settings-update.js new file mode 100644 index 0000000..f541140 --- /dev/null +++ b/js/settings-update.js @@ -0,0 +1,275 @@ +// CLI Update functionality for settings page +// Handles version checking, update dialog, and download progress + +// UI Elements +const cliVersionText = document.getElementById('cli-version-text'); +const updateCliBtn = document.getElementById('update-cli-btn'); +const cliUpdateDialog = document.getElementById('cli-update-dialog'); +const cliUpdateClose = document.getElementById('cli-update-close'); +const cliUpdateMessage = document.getElementById('cli-update-message'); +const cliUpdateDetails = document.getElementById('cli-update-details'); +const cliUpdateCancel = document.getElementById('cli-update-cancel'); +const cliUpdateConfirm = document.getElementById('cli-update-confirm'); +const cliProgressDialog = document.getElementById('cli-progress-dialog'); +const cliProgressLabel = document.getElementById('cli-progress-label'); +const cliProgressFill = document.getElementById('cli-progress-fill'); +const cliProgressText = document.getElementById('cli-progress-text'); + +// State +let currentVersion = null; +let latestVersion = null; +let updateAvailable = false; + +// Format bytes to human readable string +function formatBytes(bytes) { + if (bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; +} + +// Load and display current CLI version +async function loadCliVersion() { + const connectionType = localStorage.getItem('type') || 'local'; + + // Only show version in Local mode + if (connectionType !== 'local') { + return; + } + + try { + const storedVersion = localStorage.getItem('cliproxyapi-version'); + if (storedVersion) { + currentVersion = storedVersion; + cliVersionText.textContent = `v${currentVersion}`; + } else { + cliVersionText.textContent = 'Not installed'; + } + } catch (error) { + console.error('Error loading CLI version:', error); + cliVersionText.textContent = 'Unknown'; + } +} + +// Show update dialog +function showUpdateDialog(current, latest) { + cliUpdateMessage.textContent = `A new version of CLIProxyAPI is available!`; + cliUpdateDetails.innerHTML = ` +
+
+ Current Version: v${current} +
+
+ Latest Version: v${latest} +
+
+ `; + cliUpdateDialog.classList.add('show'); +} + +// Hide update dialog +function hideUpdateDialog() { + cliUpdateDialog.classList.remove('show'); +} + +// Show progress dialog +function showProgressDialog() { + cliProgressDialog.classList.add('show'); +} + +// Hide progress dialog +function hideProgressDialog() { + cliProgressDialog.classList.remove('show'); +} + +// Update progress display +function updateProgress(progressData) { + const percentage = Math.round(progressData.progress || 0); + cliProgressFill.style.width = `${percentage}%`; + + if (progressData.downloaded && progressData.total) { + const downloadedStr = formatBytes(progressData.downloaded); + const totalStr = formatBytes(progressData.total); + cliProgressText.textContent = `${percentage}% (${downloadedStr} / ${totalStr})`; + } else { + cliProgressText.textContent = `${percentage}%`; + } +} + +// Handle download status updates +function handleDownloadStatus(statusData) { + const status = statusData.status; + + switch (status) { + case 'checking': + cliProgressLabel.textContent = 'Checking for updates...'; + updateProgress({ progress: 0 }); + break; + + case 'starting': + cliProgressLabel.textContent = 'Starting download...'; + updateProgress({ progress: 0 }); + break; + + case 'downloading': + cliProgressLabel.textContent = 'Downloading CLIProxyAPI...'; + break; + + case 'extracting': + cliProgressLabel.textContent = 'Extracting files...'; + updateProgress({ progress: 95 }); + break; + + case 'completed': + cliProgressLabel.textContent = 'Update completed successfully!'; + updateProgress({ progress: 100 }); + + // Update version display + if (statusData.version) { + currentVersion = statusData.version; + cliVersionText.textContent = `v${currentVersion}`; + localStorage.setItem('cliproxyapi-version', currentVersion); + } + + // Hide progress dialog after a short delay + setTimeout(() => { + hideProgressDialog(); + showSuccessMessage('CLIProxyAPI updated successfully! Restarting process...'); + + // Restart CLIProxyAPI process + if (window.__TAURI__?.core?.invoke) { + window.__TAURI__.core.invoke('restart_cliproxyapi').catch(error => { + console.error('Error restarting CLIProxyAPI:', error); + showError('Failed to restart CLIProxyAPI. Please restart manually.'); + }); + } + }, 1500); + break; + + case 'latest': + hideProgressDialog(); + showSuccessMessage('CLIProxyAPI is already up to date!'); + break; + + case 'update-available': + hideProgressDialog(); + break; + + case 'failed': + hideProgressDialog(); + showError(statusData.message || 'Failed to update CLIProxyAPI'); + break; + + default: + console.log('Unknown status:', status); + } +} + +// Check for updates +async function checkForUpdates() { + const connectionType = localStorage.getItem('type') || 'local'; + + // Only works in Local mode + if (connectionType !== 'local') { + showError('Update feature is only available in Local mode'); + return; + } + + updateCliBtn.disabled = true; + updateCliBtn.textContent = 'Checking...'; + + try { + showProgressDialog(); + + // Get proxy URL from localStorage if available + const proxyUrl = localStorage.getItem('proxy-url') || null; + + // Call Tauri command to check version + const result = await window.__TAURI__.core.invoke('check_version_and_download', { + proxy: proxyUrl + }); + + if (result.success) { + currentVersion = result.version; + latestVersion = result.latestVersion; + + if (result.needsUpdate) { + // Update available + updateAvailable = true; + hideProgressDialog(); + showUpdateDialog(currentVersion, latestVersion); + } else { + // Already up to date + updateAvailable = false; + handleDownloadStatus({ status: 'latest' }); + } + } else { + throw new Error('Failed to check for updates'); + } + } catch (error) { + console.error('Error checking for updates:', error); + hideProgressDialog(); + showError('Failed to check for updates: ' + error.message); + } finally { + updateCliBtn.disabled = false; + updateCliBtn.textContent = 'Check Update'; + } +} + +// Download and install update +async function downloadUpdate() { + hideUpdateDialog(); + showProgressDialog(); + + try { + // Get proxy URL from localStorage if available + const proxyUrl = localStorage.getItem('proxy-url') || null; + + // Call Tauri command to download + const result = await window.__TAURI__.core.invoke('download_cliproxyapi', { + proxy: proxyUrl + }); + + if (!result.success) { + throw new Error(result.message || 'Download failed'); + } + } catch (error) { + console.error('Error downloading update:', error); + hideProgressDialog(); + showError('Failed to download update: ' + error.message); + } +} + +// Event listeners for update button +updateCliBtn.addEventListener('click', checkForUpdates); + +// Event listeners for update dialog +cliUpdateClose.addEventListener('click', hideUpdateDialog); +cliUpdateCancel.addEventListener('click', hideUpdateDialog); +cliUpdateConfirm.addEventListener('click', downloadUpdate); + +// Listen for download progress events from Tauri +if (window.__TAURI__?.event?.listen) { + window.__TAURI__.event.listen('download-progress', (event) => { + updateProgress(event?.payload || {}); + }); + + window.__TAURI__.event.listen('download-status', (event) => { + handleDownloadStatus(event?.payload || {}); + }); +} + +// Initialize version display on page load +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', loadCliVersion); +} else { + loadCliVersion(); +} + +// Re-load version when connection type changes (via storage event) +window.addEventListener('storage', (event) => { + if (event.key === 'type' || event.key === 'cliproxyapi-version') { + loadCliVersion(); + } +}); diff --git a/settings.html b/settings.html index dc0cac7..7251a67 100644 --- a/settings.html +++ b/settings.html @@ -42,6 +42,14 @@

EasyCLI Control Panel

Local
+ +
+
+ CLI Version: + - +
+ +
@@ -278,6 +286,42 @@

OpenAI Compatibility Providers

+ + + + + +