Loading noctua/web/__init__.py +1 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ from quart import Blueprint, render_template, websocket # Custom modules from noctua.api.basecontext import ends from noctua.utils.structure import current_log_path from noctua.utils.logger import log from .stream import BroadcastManager, StreamManager Loading noctua/web/pages/base.html +13 −5 Original line number Diff line number Diff line Loading @@ -8,25 +8,33 @@ </head> <body class="d-flex flex-column h-100" style="padding-bottom: 180px;"> <div class="container mt-4"> <div class="container"> {% block content %}{% endblock %} </div> <!-- Notification Container --> <!-- Footer Notification Area --> <div class="toast-container position-fixed bottom-0 end-0 p-3" id="notification-container" style="z-index: 1100; margin-bottom: 160px;"></div> <!-- Sticky Footer Log Terminal --> <!-- Unified Sticky Footer Log Terminal --> <footer class="footer fixed-bottom bg-black border-top border-secondary shadow-lg" style="height: 160px;"> <div class="d-flex justify-content-between align-items-center px-3 py-1 bg-dark border-bottom border-secondary"> <small class="text-muted fw-bold">SYSTEM LOGS</small> <div class="d-flex gap-3 align-items-center"> <small id="ws-status" class="text-muted">connecting...</small> <button class="btn btn-sm text-muted" onclick="document.getElementById('log-terminal').innerHTML=''">× Clear</button> </div> </div> <div id="log-terminal" class="p-2 overflow-auto" style="height: 125px; font-family: 'Courier New', monospace; font-size: 0.75rem; color: #00ff00;"> </div> </footer> <!-- Vendor Scripts --> <script src="{{ url_for('web.static', filename='js/vendor/bootstrap.bundle.min.js') }}"></script> <!-- Unified WebSocket Client (Loaded on every page) --> <script src="{{ url_for('web.static', filename='js/websocket-client.js') }}"></script> {% block scripts %}{% endblock %} </body> </html> noctua/web/pages/pages.zipdeleted 100644 → 0 −8.4 KiB File deleted. View file noctua/web/static/css/style.css +15 −0 Original line number Diff line number Diff line Loading @@ -59,3 +59,18 @@ var { .pretty-container { max-height: none; /* Let the card grow with content */ } /* Update pulse for both cards and table rows */ .pulse-update, .pulse-update td { background-color: rgba(255, 255, 255, 0.2) !important; transition: background-color 0s !important; } .widget-card, .card, tr, td { transition: background-color 0.4s ease-out; } .bg-orange { background-color: #fd7e14; /* Codice arancione standard di Bootstrap */ color: white; /* Per una migliore leggibilità */ } noctua/web/static/js/actions.js +20 −14 Original line number Diff line number Diff line Loading @@ -3,12 +3,7 @@ * Manages GET, PUT, POST, and DELETE requests. */ // System modules // Third-party modules // Custom modules const previousWidgetData = {}; // Store states for pulse detection /** * Display a non-blocking notification in the footer area. Loading Loading @@ -47,27 +42,38 @@ function showNotification(message, isError = false) { toastEl.addEventListener('hidden.bs.toast', () => toastEl.remove()); } /** * Update the UI elements (Badge and Var) with new data from the API. * Update a widget's UI and trigger a pulse animation on the card if changed. * * Parameters * ---------- * safeId : string * The unique slug identifier of the widget. * The unique identifier of the widget (e.g., 'dome-light'). * data : object * The JSON response object from the Quart API. * The JSON response containing 'response', 'raw', and 'error'. */ function updateUI(safeId, data) { const badge = document.getElementById(`badge-${safeId}`); const varEl = document.getElementById(`var-${safeId}`); const card = document.getElementById(`widget-${safeId}`) || document.getElementById(`toggle-${safeId}`) || document.getElementById(`input-${safeId}`); if (!badge && !varEl) return; const res = data.response; const raw = data.raw; const stringVal = JSON.stringify(res); // --- Pulse Logic for Widgets --- if (card && previousWidgetData[safeId] !== undefined && previousWidgetData[safeId] !== stringVal) { card.classList.add('pulse-update'); setTimeout(() => card.classList.remove('pulse-update'), 300); } previousWidgetData[safeId] = stringVal; // --- Update Content --- if (badge) { badge.textContent = (typeof res === 'object' && res !== null) ? JSON.stringify(res) : (res ?? 'N/A'); Loading @@ -75,13 +81,13 @@ function updateUI(safeId, data) { const dangerValues = ['Off', 'Closed', 'No', 'False', false, 5]; if (data.error) { badge.className = 'badge bg-danger'; badge.className = 'badge rounded-pill bg-danger'; } else if (successValues.includes(res)) { badge.className = 'badge bg-success'; badge.className = 'badge rounded-pill bg-success'; } else if (dangerValues.includes(res)) { badge.className = 'badge bg-secondary'; badge.className = 'badge rounded-pill bg-secondary'; } else { badge.className = 'badge bg-info text-dark'; badge.className = 'badge rounded-pill bg-info text-dark'; } } Loading Loading
noctua/web/__init__.py +1 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ from quart import Blueprint, render_template, websocket # Custom modules from noctua.api.basecontext import ends from noctua.utils.structure import current_log_path from noctua.utils.logger import log from .stream import BroadcastManager, StreamManager Loading
noctua/web/pages/base.html +13 −5 Original line number Diff line number Diff line Loading @@ -8,25 +8,33 @@ </head> <body class="d-flex flex-column h-100" style="padding-bottom: 180px;"> <div class="container mt-4"> <div class="container"> {% block content %}{% endblock %} </div> <!-- Notification Container --> <!-- Footer Notification Area --> <div class="toast-container position-fixed bottom-0 end-0 p-3" id="notification-container" style="z-index: 1100; margin-bottom: 160px;"></div> <!-- Sticky Footer Log Terminal --> <!-- Unified Sticky Footer Log Terminal --> <footer class="footer fixed-bottom bg-black border-top border-secondary shadow-lg" style="height: 160px;"> <div class="d-flex justify-content-between align-items-center px-3 py-1 bg-dark border-bottom border-secondary"> <small class="text-muted fw-bold">SYSTEM LOGS</small> <div class="d-flex gap-3 align-items-center"> <small id="ws-status" class="text-muted">connecting...</small> <button class="btn btn-sm text-muted" onclick="document.getElementById('log-terminal').innerHTML=''">× Clear</button> </div> </div> <div id="log-terminal" class="p-2 overflow-auto" style="height: 125px; font-family: 'Courier New', monospace; font-size: 0.75rem; color: #00ff00;"> </div> </footer> <!-- Vendor Scripts --> <script src="{{ url_for('web.static', filename='js/vendor/bootstrap.bundle.min.js') }}"></script> <!-- Unified WebSocket Client (Loaded on every page) --> <script src="{{ url_for('web.static', filename='js/websocket-client.js') }}"></script> {% block scripts %}{% endblock %} </body> </html>
noctua/web/static/css/style.css +15 −0 Original line number Diff line number Diff line Loading @@ -59,3 +59,18 @@ var { .pretty-container { max-height: none; /* Let the card grow with content */ } /* Update pulse for both cards and table rows */ .pulse-update, .pulse-update td { background-color: rgba(255, 255, 255, 0.2) !important; transition: background-color 0s !important; } .widget-card, .card, tr, td { transition: background-color 0.4s ease-out; } .bg-orange { background-color: #fd7e14; /* Codice arancione standard di Bootstrap */ color: white; /* Per una migliore leggibilità */ }
noctua/web/static/js/actions.js +20 −14 Original line number Diff line number Diff line Loading @@ -3,12 +3,7 @@ * Manages GET, PUT, POST, and DELETE requests. */ // System modules // Third-party modules // Custom modules const previousWidgetData = {}; // Store states for pulse detection /** * Display a non-blocking notification in the footer area. Loading Loading @@ -47,27 +42,38 @@ function showNotification(message, isError = false) { toastEl.addEventListener('hidden.bs.toast', () => toastEl.remove()); } /** * Update the UI elements (Badge and Var) with new data from the API. * Update a widget's UI and trigger a pulse animation on the card if changed. * * Parameters * ---------- * safeId : string * The unique slug identifier of the widget. * The unique identifier of the widget (e.g., 'dome-light'). * data : object * The JSON response object from the Quart API. * The JSON response containing 'response', 'raw', and 'error'. */ function updateUI(safeId, data) { const badge = document.getElementById(`badge-${safeId}`); const varEl = document.getElementById(`var-${safeId}`); const card = document.getElementById(`widget-${safeId}`) || document.getElementById(`toggle-${safeId}`) || document.getElementById(`input-${safeId}`); if (!badge && !varEl) return; const res = data.response; const raw = data.raw; const stringVal = JSON.stringify(res); // --- Pulse Logic for Widgets --- if (card && previousWidgetData[safeId] !== undefined && previousWidgetData[safeId] !== stringVal) { card.classList.add('pulse-update'); setTimeout(() => card.classList.remove('pulse-update'), 300); } previousWidgetData[safeId] = stringVal; // --- Update Content --- if (badge) { badge.textContent = (typeof res === 'object' && res !== null) ? JSON.stringify(res) : (res ?? 'N/A'); Loading @@ -75,13 +81,13 @@ function updateUI(safeId, data) { const dangerValues = ['Off', 'Closed', 'No', 'False', false, 5]; if (data.error) { badge.className = 'badge bg-danger'; badge.className = 'badge rounded-pill bg-danger'; } else if (successValues.includes(res)) { badge.className = 'badge bg-success'; badge.className = 'badge rounded-pill bg-success'; } else if (dangerValues.includes(res)) { badge.className = 'badge bg-secondary'; badge.className = 'badge rounded-pill bg-secondary'; } else { badge.className = 'badge bg-info text-dark'; badge.className = 'badge rounded-pill bg-info text-dark'; } } Loading