From 042ec17a50d5188eab224394cb083a4c4b996668 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Tue, 27 May 2025 11:10:36 +0200 Subject: [PATCH] Refactor dashboard frontend --- dashboard/dashboard.js | 192 +++++++++++++++++++++++++++++++++++ dashboard/index.html | 214 +--------------------------------------- dashboard/jquery.min.js | 2 + 3 files changed, 198 insertions(+), 210 deletions(-) create mode 100644 dashboard/dashboard.js create mode 100644 dashboard/jquery.min.js diff --git a/dashboard/dashboard.js b/dashboard/dashboard.js new file mode 100644 index 00000000..282c3723 --- /dev/null +++ b/dashboard/dashboard.js @@ -0,0 +1,192 @@ +class LogMessage { + constructor(message, toolNames) { + const logLevel = this.determineLogLevel(message); + const highlightedMessage = this.highlightToolNames(message, toolNames); + this.$elem = $('
').addClass('log-' + logLevel).html(highlightedMessage + '\n'); + } + + determineLogLevel(message) { + if (message.startsWith('DEBUG')) { + return 'debug'; + } else if (message.startsWith('INFO')) { + return 'info'; + } else if (message.startsWith('WARNING')) { + return 'warning'; + } else if (message.startsWith('ERROR')) { + return 'error'; + } else { + return 'default'; + } + } + + highlightToolNames(message, toolNames) { + let highlightedMessage = message; + toolNames.forEach(function(toolName) { + const regex = new RegExp('\\b' + toolName + '\\b', 'gi'); + highlightedMessage = highlightedMessage.replace(regex, '' + toolName + ''); + }); + return highlightedMessage; + } +} + +class Dashboard { + constructor() { + let self = this; + + this.toolNames = []; + this.currentMaxIdx = -1; + this.pollInterval = null; + this.$logContainer = $('#log-container'); + this.$errorContainer = $('#error-container'); + this.$loadButton = $('#load-logs'); + this.$clearButton = $('#clear-logs'); + + // register event handlers + this.$loadButton.click(this.loadLogs.bind(this)); + this.$clearButton.click(this.clearLogs.bind(this)); + + // initialize the application + this.loadToolNames().then(function() { + // Load logs on page load after tool names are loaded + self.loadLogs(); + }); + } + + displayLogMessage(message) { + $('#log-container').append(new LogMessage(message, this.toolNames).$elem); + } + + loadToolNames() { + let self = this; + return $.ajax({ + url: '/get_tool_names', + type: 'GET', + success: function(response) { + self.toolNames = response.tool_names || []; + console.log('Loaded tool names:', toolNames); + }, + error: function(xhr, status, error) { + console.error('Error loading tool names:', error); + } + }); + } + + loadLogs() { + console.log("load", this); + let self = this; + + // Disable button and show loading state + self.$loadButton.prop('disabled', true).text('Loading...'); + self.$errorContainer.empty(); + + // Make API call + $.ajax({ + url: '/get_log_messages', + type: 'POST', + contentType: 'application/json', + data: JSON.stringify({ + start_idx: 0 + }), + success: function(response) { + // Clear existing logs + self.$logContainer.empty(); + + // Update max_idx + self.currentMaxIdx = response.max_idx || -1; + + // Display each log message + if (response.messages && response.messages.length > 0) { + response.messages.forEach(function(message) { + self.displayLogMessage(message); + }); + + // Auto-scroll to bottom + const logContainer = $('#log-container')[0]; + logContainer.scrollTop = logContainer.scrollHeight; + } else { + $('#log-container').html('
No log messages found.
'); + } + + // Start periodic polling for new logs + self.startPeriodicPolling(); + }, + error: function(xhr, status, error) { + console.error('Error loading logs:', error); + self.$errorContainer.html('
Error loading logs: ' + + (xhr.responseJSON ? xhr.responseJSON.detail : error) + '
'); + }, + complete: function() { + // Re-enable button + self.$loadButton.prop('disabled', false).text('Load Logs'); + } + }); + } + + pollForNewLogs() { + let self = this; + console.log("poll", this); + $.ajax({ + url: '/get_log_messages', + type: 'POST', + contentType: 'application/json', + data: JSON.stringify({ + start_idx: self.currentMaxIdx + 1 + }), + success: function(response) { + // Only append new messages if we have any + if (response.messages && response.messages.length > 0) { + let wasAtBottom = false; + const logContainer = $('#log-container')[0]; + + // Check if user was at the bottom before adding new logs + if (logContainer.scrollHeight > 0) { + wasAtBottom = (logContainer.scrollTop + logContainer.clientHeight) >= (logContainer.scrollHeight - 10); + } + + // Append new messages + response.messages.forEach(function(message) { + self.displayLogMessage(message); + }); + + // Update max_idx + self.currentMaxIdx = response.max_idx || self.currentMaxIdx; + + // Auto-scroll to bottom if user was already at bottom + if (wasAtBottom) { + logContainer.scrollTop = logContainer.scrollHeight; + } + } else { + // Update max_idx even if no new messages + self.currentMaxIdx = response.max_idx || self.currentMaxIdx; + } + }, + error: function(xhr, status, error) { + console.error('Error polling for new logs:', error); + } + }); + } + + startPeriodicPolling() { + // Clear any existing interval + if (this.pollInterval) { + clearInterval(this.pollInterval); + } + + // Start polling every second (1000ms) + this.pollInterval = setInterval(this.pollForNewLogs.bind(this), 1000); + } + + stopPeriodicPolling() { + if (this.pollInterval) { + clearInterval(this.pollInterval); + this.pollInterval = null; + } + } + + clearLogs() { + this.stopPeriodicPolling(); + this.$logContainer.empty(); + this.$errorContainer.empty(); + this.currentMaxIdx = -1; + } +} \ No newline at end of file diff --git a/dashboard/index.html b/dashboard/index.html index 21db716b..dd968156 100644 --- a/dashboard/index.html +++ b/dashboard/index.html @@ -3,8 +3,9 @@ - Serena Log Viewer - + Serena Dashboard + +