mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-05 14:55:08 +00:00
fix(web): seven web UI bugs, and remove dead plugins_manager.js helpers (#647)
- Operation History: the plugin filter lists the installed plugin ids
instead of one option, "plugins" (Object.keys of {plugins: [...]}).
- Ctrl/Cmd+S submits the active tab's first visible form with
requestSubmit() (validation and onsubmit guards run) instead of a bare
Event on the first form in the document; skipped inside a modal dialog
and on tabs without a form.
- Overview "Check Updates" confirms like "Update Code", takes its button
explicitly (no implicit global event) and shows the server's message.
Both, and the Tools tab git pull, raise the restart-pending banner on
restart_required.
- Tools: toolsAction and diagnostics show the server's error message;
only a non-JSON body falls back to HTTP <status>.
- Installed list after uninstall: PluginAPI writes clear the throttler's
GET cache, a forced loadInstalledPlugins clears it too, and the
post-uninstall reload goes through refreshInstalledPlugins().
- Plugin widgets load from /static/plugin-widgets/ only (the other two
paths have no route).
- Raw JSON editor escapes the parse error; slider escapes value/min/max/step.
- Removed the unreferenced array-of-objects and key-value helpers from
plugins_manager.js, the textarea auto-resize and Ctrl+R handlers in
app.js, and a redundant ?v= on the plugins_manager.js script tag.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -911,7 +911,7 @@
|
||||
LEDDialog, the widgets and app-shell.js's updatePlugin at load time
|
||||
or when a card is clicked, so it must follow all of them. No other
|
||||
script defines the same globals. -->
|
||||
<script src="{{ url_for('static', filename='v3/plugins_manager.js') }}?v=20260307" defer></script>
|
||||
<script src="{{ url_for('static', filename='v3/plugins_manager.js') }}" defer></script>
|
||||
|
||||
<!-- Custom feeds table helpers live in js/widgets/custom-feeds.js (the
|
||||
deferred widget's window assignments always shadowed the inline
|
||||
|
||||
@@ -282,7 +282,7 @@
|
||||
if (data.status === 'success' && data.data) {
|
||||
const select = document.getElementById('history-plugin-filter');
|
||||
if (select) {
|
||||
const plugins = Object.keys(data.data);
|
||||
const plugins = (data.data.plugins || []).map(p => p.id).filter(Boolean);
|
||||
plugins.forEach(pluginId => {
|
||||
const option = document.createElement('option');
|
||||
option.value = pluginId;
|
||||
|
||||
@@ -424,7 +424,7 @@
|
||||
<dd class="text-lg font-semibold text-gray-900" id="ledmatrix-version">Loading...</dd>
|
||||
</div>
|
||||
</div>
|
||||
<button onclick="checkForUpdates()"
|
||||
<button onclick="checkForUpdates(this)"
|
||||
class="inline-flex items-center px-3 py-2 border border-blue-300 text-sm font-medium rounded-md text-blue-700 bg-white hover:bg-blue-50">
|
||||
<i class="fas fa-sync-alt mr-2"></i>
|
||||
Check Updates
|
||||
@@ -458,7 +458,7 @@
|
||||
hx-vals='{"action": "git_pull"}'
|
||||
hx-confirm="This will stash any local changes and update the code. Continue?"
|
||||
hx-swap="none"
|
||||
hx-on:htmx:after-request="var m='Code update completed',s='info'; try { var d=JSON.parse(event.detail.xhr.responseText); m=d.message||m; s=d.status||s; } catch(e) { s=(event.detail.xhr&&event.detail.xhr.status>=400?'error':s); } showNotification(m,s);"
|
||||
hx-on:htmx:after-request="var m='Code update completed',s='info'; try { var d=JSON.parse(event.detail.xhr.responseText); m=d.message||m; s=d.status||s; if (d.restart_required) window.notifyCodeUpdateRestart(); } catch(e) { s=(event.detail.xhr&&event.detail.xhr.status>=400?'error':s); } showNotification(m,s);"
|
||||
class="inline-flex items-center px-4 py-2 border border-gray-300 text-base font-semibold rounded-md text-gray-900 bg-white hover:bg-gray-50">
|
||||
<i class="fas fa-download mr-2"></i>
|
||||
Update Code
|
||||
@@ -575,14 +575,27 @@
|
||||
});
|
||||
})();
|
||||
|
||||
// Check for updates function
|
||||
window.checkForUpdates = function() {
|
||||
const btn = event.target.closest('button');
|
||||
// A pull only replaces files on disk; the running services keep the code
|
||||
// they loaded at boot. Same wording as the update banner in base.html.
|
||||
window.notifyCodeUpdateRestart = function() {
|
||||
if (typeof window.showRestartPending === 'function') {
|
||||
window.showRestartPending(
|
||||
'Update installed \u2014 restart the display and web ' +
|
||||
'services to run the new code');
|
||||
}
|
||||
};
|
||||
|
||||
// Check for updates function: the same git_pull (and the same confirmation)
|
||||
// as the Update Code quick action below.
|
||||
window.checkForUpdates = function(btn) {
|
||||
if (!confirm('This will stash any local changes and update the code. Continue?')) {
|
||||
return;
|
||||
}
|
||||
if (btn) {
|
||||
const originalContent = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Checking...';
|
||||
|
||||
|
||||
fetch('/api/v3/system/action', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -592,9 +605,10 @@ window.checkForUpdates = function() {
|
||||
.then(data => {
|
||||
btn.innerHTML = originalContent;
|
||||
btn.disabled = false;
|
||||
|
||||
|
||||
if (data.status === 'success') {
|
||||
showNotification('Update successful: ' + (data.stdout || 'Code updated'), 'success');
|
||||
showNotification(data.message || 'Code updated', 'success');
|
||||
if (data.restart_required) window.notifyCodeUpdateRestart();
|
||||
// Reload version after a short delay
|
||||
setTimeout(() => {
|
||||
fetch('/api/v3/system/version')
|
||||
@@ -607,7 +621,7 @@ window.checkForUpdates = function() {
|
||||
});
|
||||
}, 1000);
|
||||
} else {
|
||||
showNotification('Update failed: ' + (data.stderr || data.message || 'Unknown error'), 'error');
|
||||
showNotification(data.message || 'Update failed', 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
||||
@@ -156,7 +156,7 @@ function manualValidateJson(editorId, validationDivId) {
|
||||
<div>
|
||||
<div class="font-semibold text-red-800">✗ Invalid JSON syntax</div>
|
||||
<div class="text-sm text-red-700 mt-1">
|
||||
<strong>Error:</strong> ${e.message}
|
||||
<strong>Error:</strong> ${window.LEDEscape.html(e.message)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -451,9 +451,11 @@
|
||||
})
|
||||
.then(r => {
|
||||
if (!r.ok) {
|
||||
// Only a non-JSON body falls back to the status code; the
|
||||
// rejection carrying the server's message must not be caught.
|
||||
return r.json()
|
||||
.then(d => Promise.reject(new Error(d.message || `HTTP ${r.status}`)))
|
||||
.catch(() => Promise.reject(new Error(`HTTP ${r.status}`)));
|
||||
.catch(() => null)
|
||||
.then(d => Promise.reject(new Error((d && d.message) || `HTTP ${r.status}`)));
|
||||
}
|
||||
return r.json();
|
||||
})
|
||||
@@ -465,6 +467,14 @@
|
||||
showOutput ? (data.output || '') : '',
|
||||
showPluginDetails ? (data.details || []) : null
|
||||
);
|
||||
// A pull replaces files on disk; the services keep running the
|
||||
// code they loaded at boot until restarted.
|
||||
if (ok && action === 'git_pull' && data.restart_required &&
|
||||
typeof window.showRestartPending === 'function') {
|
||||
window.showRestartPending(
|
||||
'Update installed \u2014 restart the display and web ' +
|
||||
'services to run the new code');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
showResult(resultId, false, 'Request failed: ' + err.message);
|
||||
@@ -717,8 +727,8 @@
|
||||
fetch('/api/v3/system/status')
|
||||
.then(r => {
|
||||
if (!r.ok) return r.json()
|
||||
.then(d => Promise.reject(d.message || `HTTP ${r.status}`))
|
||||
.catch(() => Promise.reject(`HTTP ${r.status}`));
|
||||
.catch(() => null)
|
||||
.then(d => Promise.reject((d && d.message) || `HTTP ${r.status}`));
|
||||
return r.json();
|
||||
})
|
||||
.then(res => {
|
||||
|
||||
Reference in New Issue
Block a user