From 551c3a41e3c30c502d30f0cb7d9186723895bce5 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 3 Sep 2025 21:05:35 +0200 Subject: [PATCH] Display active project name in dashboard window title Fixes #595 --- src/serena/agent.py | 15 +++++++-------- src/serena/dashboard.py | 12 ++++++++++-- src/serena/resources/dashboard/dashboard.js | 9 +++++++++ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/serena/agent.py b/src/serena/agent.py index 57f95410..4d26eab6 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -135,6 +135,12 @@ class SerenaAgent: # obtain serena configuration using the decoupled factory function self.serena_config = serena_config or SerenaConfig.from_config_file() + # project-specific instances, which will be initialized upon project activation + self._active_project: Project | None = None + self.language_server: SolidLanguageServer | None = None + self.memories_manager: MemoriesManager | None = None + self.lines_read: LinesRead | None = None + # adjust log level serena_log_level = self.serena_config.log_level if Logger.root.level > serena_log_level: @@ -183,7 +189,7 @@ class SerenaAgent: # start the dashboard (web frontend), registering its log handler if self.serena_config.web_dashboard: self._dashboard_thread, port = SerenaDashboardAPI( - get_memory_log_handler(), tool_names, tool_usage_stats=self._tool_usage_stats + get_memory_log_handler(), tool_names, agent=self, tool_usage_stats=self._tool_usage_stats ).run_in_thread() dashboard_url = f"http://127.0.0.1:{port}/dashboard/index.html" log.info("Serena web dashboard started at %s", dashboard_url) @@ -224,13 +230,6 @@ class SerenaAgent: self.prompt_factory = SerenaPromptFactory() self._project_activation_callback = project_activation_callback - # project-specific instances, which will be initialized upon project activation - self._active_project: Project | None = None - self._active_project_root: str | None = None - self.language_server: SolidLanguageServer | None = None - self.memories_manager: MemoriesManager | None = None - self.lines_read: LinesRead | None = None - # set the active modes if modes is None: modes = SerenaAgentMode.load_default_modes() diff --git a/src/serena/dashboard.py b/src/serena/dashboard.py index d3085159..c4a5f91d 100644 --- a/src/serena/dashboard.py +++ b/src/serena/dashboard.py @@ -2,7 +2,7 @@ import os import socket import threading from collections.abc import Callable -from typing import Any +from typing import TYPE_CHECKING, Any from flask import Flask, Response, request, send_from_directory from pydantic import BaseModel @@ -12,6 +12,9 @@ from serena.analytics import ToolUsageStats from serena.constants import SERENA_DASHBOARD_DIR from serena.util.logging import MemoryLogHandler +if TYPE_CHECKING: + from serena.agent import SerenaAgent + log = logging.getLogger(__name__) # disable Werkzeug's logging to avoid cluttering the output @@ -25,6 +28,7 @@ class RequestLog(BaseModel): class ResponseLog(BaseModel): messages: list[str] max_idx: int + active_project: str | None = None class ResponseToolNames(BaseModel): @@ -42,11 +46,13 @@ class SerenaDashboardAPI: self, memory_log_handler: MemoryLogHandler, tool_names: list[str], + agent: "SerenaAgent", shutdown_callback: Callable[[], None] | None = None, tool_usage_stats: ToolUsageStats | None = None, ) -> None: self._memory_log_handler = memory_log_handler self._tool_names = tool_names + self._agent = agent self._shutdown_callback = shutdown_callback self._app = Flask(__name__) self._tool_usage_stats = tool_usage_stats @@ -106,7 +112,9 @@ class SerenaDashboardAPI: def _get_log_messages(self, request_log: RequestLog) -> ResponseLog: all_messages = self._memory_log_handler.get_log_messages() requested_messages = all_messages[request_log.start_idx :] if request_log.start_idx <= len(all_messages) else [] - return ResponseLog(messages=requested_messages, max_idx=len(all_messages) - 1) + project = self._agent.get_active_project() + project_name = project.project_name if project else None + return ResponseLog(messages=requested_messages, max_idx=len(all_messages) - 1, active_project=project_name) def _get_tool_names(self) -> ResponseToolNames: return ResponseToolNames(tool_names=self._tool_names) diff --git a/src/serena/resources/dashboard/dashboard.js b/src/serena/resources/dashboard/dashboard.js index 282a3330..5ce12df4 100644 --- a/src/serena/resources/dashboard/dashboard.js +++ b/src/serena/resources/dashboard/dashboard.js @@ -107,6 +107,10 @@ class Dashboard { }); } + updateTitle(activeProject) { + document.title = activeProject ? `${activeProject} – Serena Dashboard` : 'Serena Dashboard'; + } + loadLogs() { console.log("Loading logs"); let self = this; @@ -143,6 +147,8 @@ class Dashboard { $('#log-container').html('
No log messages found.
'); } + self.updateTitle(response.active_project); + // Start periodic polling for new logs self.startPeriodicPolling(); }, @@ -196,6 +202,9 @@ class Dashboard { // Update max_idx even if no new messages self.currentMaxIdx = response.max_idx || self.currentMaxIdx; } + + // Update window title with active project + self.updateTitle(response.active_project); }, error: function(xhr, status, error) { console.error('Error polling for new logs:', error);