mirror of
https://github.com/smittix/intercept.git
synced 2026-05-05 11:49:11 -07:00
feat: ship platform UX and reliability upgrades
This commit is contained in:
@@ -78,13 +78,14 @@ const Updater = {
|
||||
* Show update toast notification
|
||||
* @param {Object} data - Update data from server
|
||||
*/
|
||||
showUpdateToast(data) {
|
||||
// Remove existing toast if present
|
||||
this.hideToast();
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'update-toast';
|
||||
toast.innerHTML = `
|
||||
showUpdateToast(data) {
|
||||
// Remove existing toast if present
|
||||
this.hideToast();
|
||||
const latestVersion = this._escape(data.latest_version || '');
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'update-toast';
|
||||
toast.innerHTML = `
|
||||
<div class="update-toast-indicator"></div>
|
||||
<div class="update-toast-content">
|
||||
<div class="update-toast-header">
|
||||
@@ -97,11 +98,11 @@ const Updater = {
|
||||
</span>
|
||||
<span class="update-toast-title">Update Available</span>
|
||||
<button class="update-toast-close" onclick="Updater.dismissUpdate()">×</button>
|
||||
</div>
|
||||
<div class="update-toast-body">
|
||||
Version <strong>${data.latest_version}</strong> is ready
|
||||
</div>
|
||||
<div class="update-toast-actions">
|
||||
</div>
|
||||
<div class="update-toast-body">
|
||||
Version <strong>${latestVersion}</strong> is ready
|
||||
</div>
|
||||
<div class="update-toast-actions">
|
||||
<button class="update-toast-btn update-toast-btn-primary" onclick="Updater.showUpdateModal()">
|
||||
View Details
|
||||
</button>
|
||||
@@ -172,14 +173,17 @@ const Updater = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove existing modal if present
|
||||
this.hideModal();
|
||||
|
||||
const data = this._updateData;
|
||||
const releaseNotes = this._formatReleaseNotes(data.release_notes || 'No release notes available.');
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'update-modal-overlay';
|
||||
// Remove existing modal if present
|
||||
this.hideModal();
|
||||
|
||||
const data = this._updateData;
|
||||
const releaseNotes = this._formatReleaseNotes(data.release_notes || 'No release notes available.');
|
||||
const safeCurrentVersion = this._escape(data.current_version || '');
|
||||
const safeLatestVersion = this._escape(data.latest_version || '');
|
||||
const safeReleaseUrl = this._safeUrl(data.release_url || '');
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'update-modal-overlay';
|
||||
modal.onclick = (e) => {
|
||||
if (e.target === modal) this.hideModal();
|
||||
};
|
||||
@@ -201,21 +205,21 @@ const Updater = {
|
||||
</div>
|
||||
<div class="update-modal-body">
|
||||
<div class="update-version-info">
|
||||
<div class="update-version-current">
|
||||
<span class="update-version-label">Current</span>
|
||||
<span class="update-version-value">v${data.current_version}</span>
|
||||
</div>
|
||||
<div class="update-version-current">
|
||||
<span class="update-version-label">Current</span>
|
||||
<span class="update-version-value">v${safeCurrentVersion}</span>
|
||||
</div>
|
||||
<div class="update-version-arrow">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="5" y1="12" x2="19" y2="12"/>
|
||||
<polyline points="12 5 19 12 12 19"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="update-version-latest">
|
||||
<span class="update-version-label">Latest</span>
|
||||
<span class="update-version-value update-version-new">v${data.latest_version}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="update-version-latest">
|
||||
<span class="update-version-label">Latest</span>
|
||||
<span class="update-version-value update-version-new">v${safeLatestVersion}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="update-section">
|
||||
<div class="update-section-title">Release Notes</div>
|
||||
@@ -249,11 +253,11 @@ const Updater = {
|
||||
</div>
|
||||
|
||||
<div class="update-result" id="updateResult" style="display: none;"></div>
|
||||
</div>
|
||||
<div class="update-modal-footer">
|
||||
<a href="${data.release_url || '#'}" target="_blank" class="update-modal-link" ${!data.release_url ? 'style="display:none"' : ''}>
|
||||
View on GitHub
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
|
||||
</div>
|
||||
<div class="update-modal-footer">
|
||||
<a href="${safeReleaseUrl || '#'}" target="_blank" class="update-modal-link" ${!safeReleaseUrl ? 'style="display:none"' : ''}>
|
||||
View on GitHub
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
||||
<polyline points="15 3 21 3 21 9"/>
|
||||
<line x1="10" y1="14" x2="21" y2="3"/>
|
||||
@@ -357,14 +361,16 @@ const Updater = {
|
||||
/**
|
||||
* Show update result
|
||||
*/
|
||||
_showResult(resultEl, success, data, isManual = false) {
|
||||
if (!resultEl) return;
|
||||
|
||||
resultEl.style.display = 'block';
|
||||
|
||||
if (success) {
|
||||
if (data.updated) {
|
||||
let message = '<strong>Update successful!</strong><br>Please restart the application to complete the update.';
|
||||
_showResult(resultEl, success, data, isManual = false) {
|
||||
if (!resultEl) return;
|
||||
|
||||
resultEl.style.display = 'block';
|
||||
const safeMessage = this._escape(data.message || data.error || 'An error occurred during the update.');
|
||||
const safeDetails = data.details ? this._escape(String(data.details).substring(0, 200)) : '';
|
||||
|
||||
if (success) {
|
||||
if (data.updated) {
|
||||
let message = '<strong>Update successful!</strong><br>Please restart the application to complete the update.';
|
||||
|
||||
if (data.requirements_changed) {
|
||||
message += '<br><br><strong>Dependencies changed!</strong> Run:<br><code>pip install -r requirements.txt</code>';
|
||||
@@ -380,22 +386,22 @@ const Updater = {
|
||||
</div>
|
||||
<div class="update-result-text">${message}</div>
|
||||
`;
|
||||
} else {
|
||||
resultEl.className = 'update-result update-result-info';
|
||||
resultEl.innerHTML = `
|
||||
} else {
|
||||
resultEl.className = 'update-result update-result-info';
|
||||
resultEl.innerHTML = `
|
||||
<div class="update-result-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="16" x2="12" y2="12"/>
|
||||
<line x1="12" y1="8" x2="12.01" y2="8"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="update-result-text">${data.message || 'Already up to date.'}</div>
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
if (isManual) {
|
||||
resultEl.className = 'update-result update-result-warning';
|
||||
</div>
|
||||
<div class="update-result-text">${this._escape(data.message || 'Already up to date.')}</div>
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
if (isManual) {
|
||||
resultEl.className = 'update-result update-result-warning';
|
||||
resultEl.innerHTML = `
|
||||
<div class="update-result-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
@@ -403,14 +409,14 @@ const Updater = {
|
||||
<line x1="12" y1="9" x2="12" y2="13"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="update-result-text">
|
||||
<strong>Manual update required</strong><br>
|
||||
${data.message || 'Please download the latest release from GitHub.'}
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
resultEl.className = 'update-result update-result-error';
|
||||
</div>
|
||||
<div class="update-result-text">
|
||||
<strong>Manual update required</strong><br>
|
||||
${safeMessage || 'Please download the latest release from GitHub.'}
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
resultEl.className = 'update-result update-result-error';
|
||||
resultEl.innerHTML = `
|
||||
<div class="update-result-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
@@ -418,16 +424,16 @@ const Updater = {
|
||||
<line x1="15" y1="9" x2="9" y2="15"/>
|
||||
<line x1="9" y1="9" x2="15" y2="15"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="update-result-text">
|
||||
<strong>Update failed</strong><br>
|
||||
${data.message || data.error || 'An error occurred during the update.'}
|
||||
${data.details ? '<br><code style="font-size: 10px; margin-top: 8px; display: block;">' + data.details.substring(0, 200) + '</code>' : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
},
|
||||
</div>
|
||||
<div class="update-result-text">
|
||||
<strong>Update failed</strong><br>
|
||||
${safeMessage}
|
||||
${safeDetails ? '<br><code style="font-size: 10px; margin-top: 8px; display: block;">' + safeDetails + '</code>' : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Format release notes (basic markdown to HTML)
|
||||
@@ -461,11 +467,33 @@ const Updater = {
|
||||
// Line breaks
|
||||
.replace(/\n/g, '<br>');
|
||||
|
||||
// Wrap list items
|
||||
html = html.replace(/(<li>.*<\/li>)+/g, '<ul>$&</ul>');
|
||||
|
||||
return '<p>' + html + '</p>';
|
||||
},
|
||||
// Wrap list items
|
||||
html = html.replace(/(<li>.*<\/li>)+/g, '<ul>$&</ul>');
|
||||
|
||||
return '<p>' + html + '</p>';
|
||||
},
|
||||
|
||||
_escape(value) {
|
||||
return String(value == null ? '' : value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
},
|
||||
|
||||
_safeUrl(url) {
|
||||
if (!url) return '';
|
||||
try {
|
||||
const parsed = new URL(url, window.location.origin);
|
||||
if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
|
||||
return parsed.href;
|
||||
}
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Manual trigger for settings panel
|
||||
|
||||
Reference in New Issue
Block a user