From 2cd8d91bb950d795b5a95f8e2b94f57f10a1e580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=8C=AF=E6=B0=91?= Date: Thu, 26 Jun 2025 09:43:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/locales/en/translation.json | 4 + .../web/locales/zh-CN/translation.json | 4 + .../web/locales/zh-TW/translation.json | 4 + src/mcp_feedback_enhanced/web/main.py | 173 +++++------------- .../web/models/feedback_session.py | 86 +++++++-- .../web/routes/main_routes.py | 35 ++++ .../web/static/js/app.js | 100 +++++----- .../modules/session/session-data-manager.js | 71 +++++-- .../js/modules/session/session-ui-renderer.js | 11 +- .../static/js/modules/utils/status-utils.js | 2 +- 10 files changed, 281 insertions(+), 209 deletions(-) diff --git a/src/mcp_feedback_enhanced/web/locales/en/translation.json b/src/mcp_feedback_enhanced/web/locales/en/translation.json index 7f268f8..1e7b429 100644 --- a/src/mcp_feedback_enhanced/web/locales/en/translation.json +++ b/src/mcp_feedback_enhanced/web/locales/en/translation.json @@ -156,6 +156,10 @@ "submitted": { "title": "Submitted", "message": "Waiting for next MCP call" + }, + "completed": { + "title": "Completed", + "message": "Session completed" } }, "notifications": { diff --git a/src/mcp_feedback_enhanced/web/locales/zh-CN/translation.json b/src/mcp_feedback_enhanced/web/locales/zh-CN/translation.json index 5f85cb3..4522fa7 100644 --- a/src/mcp_feedback_enhanced/web/locales/zh-CN/translation.json +++ b/src/mcp_feedback_enhanced/web/locales/zh-CN/translation.json @@ -156,6 +156,10 @@ "submitted": { "title": "反馈已提交", "message": "等待下次 MCP 调用" + }, + "completed": { + "title": "已完成", + "message": "会话已完成" } }, "notifications": { diff --git a/src/mcp_feedback_enhanced/web/locales/zh-TW/translation.json b/src/mcp_feedback_enhanced/web/locales/zh-TW/translation.json index 17de62d..e623206 100644 --- a/src/mcp_feedback_enhanced/web/locales/zh-TW/translation.json +++ b/src/mcp_feedback_enhanced/web/locales/zh-TW/translation.json @@ -161,6 +161,10 @@ "submitted": { "title": "回饋已提交", "message": "等待下次 MCP 調用" + }, + "completed": { + "title": "已完成", + "message": "會話已完成" } }, "notifications": { diff --git a/src/mcp_feedback_enhanced/web/main.py b/src/mcp_feedback_enhanced/web/main.py index 0ae1141..1887ebb 100644 --- a/src/mcp_feedback_enhanced/web/main.py +++ b/src/mcp_feedback_enhanced/web/main.py @@ -318,25 +318,46 @@ class WebUIManager: def create_session(self, project_directory: str, summary: str) -> str: """創建新的回饋會話 - 重構為單一活躍會話模式,保留標籤頁狀態""" - # 保存舊會話的 WebSocket 連接以便發送更新通知 + # 保存舊會話的引用和 WebSocket 連接 + old_session = self.current_session old_websocket = None - if self.current_session and self.current_session.websocket: - old_websocket = self.current_session.websocket + if old_session and old_session.websocket: + old_websocket = old_session.websocket debug_log("保存舊會話的 WebSocket 連接以發送更新通知") - # 如果已有活躍會話,先保存其標籤頁狀態到全局狀態 - if self.current_session: - debug_log("保存現有會話的標籤頁狀態並清理會話") - # 保存標籤頁狀態到全局 - if hasattr(self.current_session, "active_tabs"): - self._merge_tabs_to_global(self.current_session.active_tabs) - - # 同步清理會話資源(但保留 WebSocket 連接) - self.current_session._cleanup_sync() - + # 創建新會話 session_id = str(uuid.uuid4()) session = WebFeedbackSession(session_id, project_directory, summary) + # 如果有舊會話,處理狀態轉換和清理 + if old_session: + debug_log(f"處理舊會話 {old_session.session_id} 的狀態轉換,當前狀態: {old_session.status.value}") + + # 保存標籤頁狀態到全局 + if hasattr(old_session, "active_tabs"): + self._merge_tabs_to_global(old_session.active_tabs) + + # 如果舊會話是已提交狀態,進入下一步(已完成) + if old_session.status == SessionStatus.FEEDBACK_SUBMITTED: + debug_log(f"舊會話 {old_session.session_id} 進入下一步:已提交 → 已完成") + success = old_session.next_step("反饋已處理,會話完成") + if success: + debug_log(f"✅ 舊會話 {old_session.session_id} 成功進入已完成狀態") + else: + debug_log(f"❌ 舊會話 {old_session.session_id} 無法進入下一步") + else: + debug_log(f"舊會話 {old_session.session_id} 狀態為 {old_session.status.value},無需轉換") + + # 確保舊會話仍在字典中(用於API獲取) + if old_session.session_id in self.sessions: + debug_log(f"舊會話 {old_session.session_id} 仍在會話字典中") + else: + debug_log(f"⚠️ 舊會話 {old_session.session_id} 不在會話字典中,重新添加") + self.sessions[old_session.session_id] = old_session + + # 同步清理會話資源(但保留 WebSocket 連接) + old_session._cleanup_sync() + # 將全局標籤頁狀態繼承到新會話 session.active_tabs = self.global_active_tabs.copy() @@ -348,25 +369,11 @@ class WebUIManager: debug_log(f"創建新的活躍會話: {session_id}") debug_log(f"繼承 {len(session.active_tabs)} 個活躍標籤頁") - # 處理會話更新通知 + # 處理WebSocket連接轉移 if old_websocket: - # 有舊連接,立即發送會話更新通知並轉移連接 - self._old_websocket_for_update = old_websocket - self._new_session_for_update = session - debug_log("已保存舊 WebSocket 連接,準備發送會話更新通知") - - # 立即發送會話更新通知 - import asyncio - - try: - # 在後台任務中發送通知並轉移連接 - asyncio.create_task(self._send_immediate_session_update()) - except Exception as e: - debug_log(f"創建會話更新任務失敗: {e}") - # 即使任務創建失敗,也要嘗試直接轉移連接 - session.websocket = old_websocket - debug_log("任務創建失敗,直接轉移 WebSocket 連接到新會話") - self._pending_session_update = True + # 直接轉移連接到新會話,消息發送由 smart_open_browser 統一處理 + session.websocket = old_websocket + debug_log("已將舊 WebSocket 連接轉移到新會話") else: # 沒有舊連接,標記需要發送會話更新通知(當新 WebSocket 連接建立時) self._pending_session_update = True @@ -682,107 +689,9 @@ class WebUIManager: else: debug_log("沒有活躍的桌面應用程式實例") - async def notify_session_update(self, session): - """向活躍標籤頁發送會話更新通知""" - try: - # 檢查是否有活躍的 WebSocket 連接 - if session.websocket: - # 直接通過當前會話的 WebSocket 發送 - await session.websocket.send_json( - { - "type": "session_updated", - "action": "new_session_created", - "message": "新會話已創建,正在更新頁面內容", - "session_info": { - "project_directory": session.project_directory, - "summary": session.summary, - "session_id": session.session_id, - }, - } - ) - debug_log("會話更新通知已通過 WebSocket 發送") - else: - # 沒有活躍連接,設置待更新標記 - self._pending_session_update = True - debug_log("沒有活躍 WebSocket 連接,設置待更新標記") - except Exception as e: - debug_log(f"發送會話更新通知失敗: {e}") - # 設置待更新標記作為備用方案 - self._pending_session_update = True - async def _send_immediate_session_update(self): - """立即發送會話更新通知(使用舊的 WebSocket 連接)""" - try: - # 檢查是否有保存的舊 WebSocket 連接 - if hasattr(self, "_old_websocket_for_update") and hasattr( - self, "_new_session_for_update" - ): - old_websocket = self._old_websocket_for_update - new_session = self._new_session_for_update - # 改進的連接有效性檢查 - websocket_valid = False - if old_websocket: - try: - # 檢查 WebSocket 連接狀態 - if hasattr(old_websocket, "client_state"): - websocket_valid = ( - old_websocket.client_state - != old_websocket.client_state.DISCONNECTED - ) - else: - # 如果沒有 client_state 屬性,嘗試發送測試消息來檢查連接 - websocket_valid = True - except Exception as check_error: - debug_log(f"檢查 WebSocket 連接狀態失敗: {check_error}") - websocket_valid = False - if websocket_valid: - try: - # 發送會話更新通知 - await old_websocket.send_json( - { - "type": "session_updated", - "action": "new_session_created", - "message": "新會話已創建,正在更新頁面內容", - "session_info": { - "project_directory": new_session.project_directory, - "summary": new_session.summary, - "session_id": new_session.session_id, - }, - } - ) - debug_log("已通過舊 WebSocket 連接發送會話更新通知") - - # 延遲一小段時間讓前端處理消息 - await asyncio.sleep(0.2) - - # 將 WebSocket 連接轉移到新會話 - new_session.websocket = old_websocket - debug_log("已將 WebSocket 連接轉移到新會話") - - except Exception as send_error: - debug_log(f"發送會話更新通知失敗: {send_error}") - # 如果發送失敗,仍然嘗試轉移連接 - new_session.websocket = old_websocket - debug_log("發送失敗但仍轉移 WebSocket 連接到新會話") - else: - debug_log("舊 WebSocket 連接無效,設置待更新標記") - self._pending_session_update = True - - # 清理臨時變數 - delattr(self, "_old_websocket_for_update") - delattr(self, "_new_session_for_update") - - else: - # 沒有舊連接,設置待更新標記 - self._pending_session_update = True - debug_log("沒有舊 WebSocket 連接,設置待更新標記") - - except Exception as e: - debug_log(f"立即發送會話更新通知失敗: {e}") - # 回退到待更新標記 - self._pending_session_update = True async def _safe_close_websocket(self, websocket): """安全關閉 WebSocket 連接,避免事件循環衝突 - 僅在連接已轉移後調用""" @@ -1127,7 +1036,7 @@ async def launch_web_feedback_ui( """ manager = get_web_ui_manager() - # 創建或更新當前活躍會話 + # 創建新會話(每次AI調用都應該創建新會話) manager.create_session(project_directory, summary) session = manager.get_current_session() @@ -1154,9 +1063,9 @@ async def launch_web_feedback_ui( debug_log(f"[DEBUG] 服務器地址: {feedback_url}") - # 如果檢測到活躍標籤頁但沒有開啟新視窗,立即發送會話更新通知 + # 如果檢測到活躍標籤頁,消息已在 smart_open_browser 中發送,無需額外處理 if has_active_tabs: - debug_log("檢測到活躍標籤頁,跳過額外的會話更新通知(已在 smart_open_browser 中發送)") + debug_log("檢測到活躍標籤頁,會話更新通知已發送") try: # 等待用戶回饋,傳遞 timeout 參數 diff --git a/src/mcp_feedback_enhanced/web/models/feedback_session.py b/src/mcp_feedback_enhanced/web/models/feedback_session.py index 7aa915b..7821a61 100644 --- a/src/mcp_feedback_enhanced/web/models/feedback_session.py +++ b/src/mcp_feedback_enhanced/web/models/feedback_session.py @@ -29,15 +29,13 @@ from ...utils.resource_manager import get_resource_manager, register_process class SessionStatus(Enum): - """會話狀態枚舉""" + """會話狀態枚舉 - 單向流轉設計""" WAITING = "waiting" # 等待中 - ACTIVE = "active" # 活躍中 FEEDBACK_SUBMITTED = "feedback_submitted" # 已提交反饋 COMPLETED = "completed" # 已完成 - TIMEOUT = "timeout" # 超時 - ERROR = "error" # 錯誤 - EXPIRED = "expired" # 已過期 + ERROR = "error" # 錯誤(終態) + EXPIRED = "expired" # 已過期(終態) class CleanupReason(Enum): @@ -174,21 +172,79 @@ class WebFeedbackSession: f"會話 {self.session_id} 初始化完成,自動清理延遲: {auto_cleanup_delay}秒,最大空閒: {max_idle_time}秒" ) - def update_status(self, status: SessionStatus, message: str | None = None): - """更新會話狀態""" - self.status = status + def next_step(self, message: str | None = None) -> bool: + """進入下一個狀態 - 單向流轉,不可倒退""" + old_status = self.status + + # 定義狀態流轉路徑 + next_status_map = { + SessionStatus.WAITING: SessionStatus.FEEDBACK_SUBMITTED, + SessionStatus.FEEDBACK_SUBMITTED: SessionStatus.COMPLETED, + SessionStatus.COMPLETED: None, # 終態 + SessionStatus.ERROR: None, # 終態 + SessionStatus.EXPIRED: None # 終態 + } + + next_status = next_status_map.get(self.status) + + if next_status is None: + debug_log(f"⚠️ 會話 {self.session_id} 已處於終態 {self.status.value},無法進入下一步") + return False + + # 執行狀態轉換 + self.status = next_status if message: self.status_message = message - # 統一使用 time.time() + else: + # 默認消息 + default_messages = { + SessionStatus.FEEDBACK_SUBMITTED: "用戶已提交反饋", + SessionStatus.COMPLETED: "會話已完成" + } + self.status_message = default_messages.get(next_status, "狀態已更新") + self.last_activity = time.time() - # 如果會話變為活躍狀態,重置清理定時器 - if status in [SessionStatus.ACTIVE, SessionStatus.FEEDBACK_SUBMITTED]: + # 如果會話變為已提交狀態,重置清理定時器 + if next_status == SessionStatus.FEEDBACK_SUBMITTED: self._schedule_auto_cleanup() debug_log( - f"會話 {self.session_id} 狀態更新: {status.value} - {self.status_message}" + f"✅ 會話 {self.session_id} 狀態流轉: {old_status.value} → {next_status.value} - {self.status_message}" ) + return True + + def set_error(self, message: str = "會話發生錯誤") -> bool: + """設置錯誤狀態(特殊方法,可從任何狀態進入)""" + old_status = self.status + self.status = SessionStatus.ERROR + self.status_message = message + self.last_activity = time.time() + + debug_log( + f"❌ 會話 {self.session_id} 設置為錯誤狀態: {old_status.value} → {self.status.value} - {message}" + ) + return True + + def set_expired(self, message: str = "會話已過期") -> bool: + """設置過期狀態(特殊方法,可從任何狀態進入)""" + old_status = self.status + self.status = SessionStatus.EXPIRED + self.status_message = message + self.last_activity = time.time() + + debug_log( + f"⏰ 會話 {self.session_id} 設置為過期狀態: {old_status.value} → {self.status.value} - {message}" + ) + return True + + def can_proceed(self) -> bool: + """檢查是否可以進入下一步""" + return self.status in [SessionStatus.WAITING, SessionStatus.FEEDBACK_SUBMITTED] + + def is_terminal(self) -> bool: + """檢查是否處於終態""" + return self.status in [SessionStatus.COMPLETED, SessionStatus.ERROR, SessionStatus.EXPIRED] def get_status_info(self) -> dict[str, Any]: """獲取會話狀態信息""" @@ -404,10 +460,8 @@ class WebFeedbackSession: self.settings = settings or {} self.images = self._process_images(images) - # 更新狀態為已提交反饋 - self.update_status( - SessionStatus.FEEDBACK_SUBMITTED, "已送出反饋,等待下次 MCP 調用" - ) + # 進入下一步:等待中 → 已提交反饋 + self.next_step("已送出反饋,等待下次 MCP 調用") self.feedback_completed.set() diff --git a/src/mcp_feedback_enhanced/web/routes/main_routes.py b/src/mcp_feedback_enhanced/web/routes/main_routes.py index ee263db..a4d95d8 100644 --- a/src/mcp_feedback_enhanced/web/routes/main_routes.py +++ b/src/mcp_feedback_enhanced/web/routes/main_routes.py @@ -157,6 +157,41 @@ def setup_routes(manager: "WebUIManager"): } ) + @manager.app.get("/api/all-sessions") + async def get_all_sessions(): + """獲取所有會話的實時狀態""" + try: + sessions_data = [] + + # 獲取所有會話的實時狀態 + for session_id, session in manager.sessions.items(): + session_info = { + "session_id": session.session_id, + "project_directory": session.project_directory, + "summary": session.summary, + "status": session.status.value, + "status_message": session.status_message, + "created_at": int(session.created_at * 1000), # 轉換為毫秒 + "last_activity": int(session.last_activity * 1000), + "feedback_completed": session.feedback_completed.is_set(), + "has_websocket": session.websocket is not None, + "is_current": session == manager.current_session + } + sessions_data.append(session_info) + + # 按創建時間排序(最新的在前) + sessions_data.sort(key=lambda x: x["created_at"], reverse=True) + + debug_log(f"返回 {len(sessions_data)} 個會話的實時狀態") + return JSONResponse(content={"sessions": sessions_data}) + + except Exception as e: + debug_log(f"獲取所有會話狀態失敗: {e}") + return JSONResponse( + status_code=500, + content={"error": f"獲取會話狀態失敗: {e!s}"} + ) + @manager.app.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): """WebSocket 端點 - 重構後移除 session_id 依賴""" diff --git a/src/mcp_feedback_enhanced/web/static/js/app.js b/src/mcp_feedback_enhanced/web/static/js/app.js index 53d7de1..c708028 100644 --- a/src/mcp_feedback_enhanced/web/static/js/app.js +++ b/src/mcp_feedback_enhanced/web/static/js/app.js @@ -686,7 +686,7 @@ // 檢查是否是新會話創建的通知 if (data.action === 'new_session_created') { - console.log('🆕 檢測到新會話創建,準備刷新頁面顯示新內容'); + console.log('🆕 檢測到新會話創建,完全刷新頁面以確保狀態同步'); // 播放音效通知 if (this.audioManager) { @@ -699,9 +699,9 @@ window.MCPFeedback.Utils.CONSTANTS.MESSAGE_SUCCESS ); - // 延遲一小段時間讓用戶看到通知,然後刷新頁面 + // 延遲一小段時間讓用戶看到通知,然後完全刷新頁面 setTimeout(function() { - console.log('🔄 刷新頁面以顯示新會話內容'); + console.log('🔄 完全刷新頁面以顯示新會話內容'); window.location.reload(); }, 1500); @@ -831,7 +831,16 @@ * 處理狀態更新(原始版本,供防抖使用) */ FeedbackApp.prototype._originalHandleStatusUpdate = function(statusInfo) { - console.log('處理狀態更新:', statusInfo); + console.log('📊 處理狀態更新:', statusInfo); + + const sessionId = statusInfo.session_id; + console.log('🔍 狀態更新詳情:', { + currentSessionId: this.currentSessionId, + newSessionId: sessionId, + status: statusInfo.status, + message: statusInfo.message, + isNewSession: sessionId !== this.currentSessionId + }); // 更新 SessionManager 的狀態資訊 if (this.sessionManager && this.sessionManager.updateStatusInfo) { @@ -844,43 +853,36 @@ document.title = 'MCP Feedback - ' + projectName; } - // 提取會話 ID - const sessionId = statusInfo.session_id || this.currentSessionId; + // 使用之前已聲明的 sessionId - // 根據狀態更新 UI + // 前端只管理會話ID,所有狀態都從服務器獲取 + console.log('📊 收到服務器狀態更新:', statusInfo.status, '會話ID:', sessionId); + + // 更新當前會話ID + if (sessionId) { + this.currentSessionId = sessionId; + console.log('🔄 更新當前會話ID:', sessionId.substring(0, 8) + '...'); + } + + // 根據服務器狀態更新消息顯示(不修改前端狀態) switch (statusInfo.status) { case 'feedback_submitted': - this.uiManager.setFeedbackState(window.MCPFeedback.Utils.CONSTANTS.FEEDBACK_SUBMITTED, sessionId); const submittedMessage = window.i18nManager ? window.i18nManager.t('feedback.submittedWaiting') : '已送出反饋,等待下次 MCP 調用...'; this.updateSummaryStatus(submittedMessage); break; - - case 'active': case 'waiting': - // 檢查是否是新會話 - if (sessionId && sessionId !== this.currentSessionId) { - // 新會話:重置為等待狀態 - this.uiManager.setFeedbackState(window.MCPFeedback.Utils.CONSTANTS.FEEDBACK_WAITING, sessionId); - } else { - // 同一會話:保護已提交狀態,避免被覆蓋 - const currentState = this.uiManager.getFeedbackState(); - if (currentState !== window.MCPFeedback.Utils.CONSTANTS.FEEDBACK_SUBMITTED) { - this.uiManager.setFeedbackState(window.MCPFeedback.Utils.CONSTANTS.FEEDBACK_WAITING, sessionId); - } else { - console.log('🔒 保護已提交狀態,不重置為等待狀態'); - } - } + const waitingMessage = window.i18nManager ? window.i18nManager.t('feedback.waitingForUser') : '等待用戶回饋...'; + this.updateSummaryStatus(waitingMessage); - if (statusInfo.status === 'waiting') { - const waitingMessage = window.i18nManager ? window.i18nManager.t('feedback.waitingForUser') : '等待用戶回饋...'; - this.updateSummaryStatus(waitingMessage); - - // 檢查並啟動自動提交(如果條件滿足) - const self = this; - setTimeout(function() { - self.checkAndStartAutoSubmit(); - }, 100); // 短暫延遲確保狀態更新完成 - } + // 檢查並啟動自動提交(如果條件滿足) + const self = this; + setTimeout(function() { + self.checkAndStartAutoSubmit(); + }, 100); + break; + case 'completed': + const completedMessage = window.i18nManager ? window.i18nManager.t('feedback.completed') : '會話已完成'; + this.updateSummaryStatus(completedMessage); break; } }; @@ -923,10 +925,15 @@ * 檢查是否可以提交回饋 */ FeedbackApp.prototype.canSubmitFeedback = function() { - return this.webSocketManager && - this.webSocketManager.isReady() && - this.uiManager && - this.uiManager.getFeedbackState() === window.MCPFeedback.Utils.CONSTANTS.FEEDBACK_WAITING; + // 簡化檢查:只檢查WebSocket連接,狀態由服務器端驗證 + const wsReady = this.webSocketManager && this.webSocketManager.isReady(); + + console.log('🔍 提交檢查:', { + wsReady: wsReady, + sessionId: this.currentSessionId + }); + + return wsReady; }; /** @@ -1203,14 +1210,9 @@ FeedbackApp.prototype.handleSessionUpdate = function(sessionData) { console.log('🔄 處理自動檢測到的會話更新:', sessionData); - // 更新當前會話 ID + // 只更新當前會話 ID,不管理狀態 this.currentSessionId = sessionData.session_id; - // 重置回饋狀態 - if (this.uiManager) { - this.uiManager.setFeedbackState(window.MCPFeedback.Utils.CONSTANTS.FEEDBACK_WAITING, sessionData.session_id); - } - // 局部更新頁面內容 this.refreshPageContent(); }; @@ -1233,9 +1235,17 @@ .then(function(sessionData) { console.log('📥 獲取到最新會話資料:', sessionData); - // 重置回饋狀態 + // 檢查並保護已提交狀態 if (sessionData.session_id && self.uiManager) { - self.uiManager.setFeedbackState(window.MCPFeedback.Utils.CONSTANTS.FEEDBACK_WAITING, sessionData.session_id); + const currentState = self.uiManager.getFeedbackState(); + if (currentState !== window.MCPFeedback.Utils.CONSTANTS.FEEDBACK_SUBMITTED) { + self.uiManager.setFeedbackState(window.MCPFeedback.Utils.CONSTANTS.FEEDBACK_WAITING, sessionData.session_id); + console.log('🔄 局部更新:重置回饋狀態為等待中'); + } else { + console.log('🔒 局部更新:保護已提交狀態,不重置'); + // 只更新會話ID,保持已提交狀態 + self.uiManager.setFeedbackState(window.MCPFeedback.Utils.CONSTANTS.FEEDBACK_SUBMITTED, sessionData.session_id); + } } // 更新 AI 摘要內容 diff --git a/src/mcp_feedback_enhanced/web/static/js/modules/session/session-data-manager.js b/src/mcp_feedback_enhanced/web/static/js/modules/session/session-data-manager.js index 550b076..2c3f061 100644 --- a/src/mcp_feedback_enhanced/web/static/js/modules/session/session-data-manager.js +++ b/src/mcp_feedback_enhanced/web/static/js/modules/session/session-data-manager.js @@ -62,9 +62,13 @@ if (this.currentSession && this.currentSession.session_id) { console.log('📊 檢測到會話 ID 變更,處理舊會話:', this.currentSession.session_id, '->', sessionData.session_id); - // 將舊會話標記為完成並加入歷史記錄 + // 將舊會話加入歷史記錄,保持其原有狀態 const oldSession = Object.assign({}, this.currentSession); - oldSession.status = 'completed'; + + // 完全保持舊會話的原有狀態,不做任何修改 + // 讓服務器端負責狀態轉換,前端只負責顯示 + console.log('📊 保持舊會話的原有狀態:', oldSession.status); + oldSession.completed_at = TimeUtils.getCurrentTimestamp(); // 計算持續時間 @@ -589,23 +593,25 @@ }; /** - * 從伺服器載入會話歷史 + * 從伺服器載入會話歷史(包含實時狀態) */ SessionDataManager.prototype.loadFromServer = function() { const self = this; - fetch('/api/load-session-history') + // 首先嘗試獲取實時會話狀態 + fetch('/api/all-sessions') .then(function(response) { if (response.ok) { return response.json(); } else { - throw new Error('伺服器回應錯誤: ' + response.status); + throw new Error('獲取實時會話狀態失敗: ' + response.status); } }) .then(function(data) { if (data && Array.isArray(data.sessions)) { + // 使用實時會話狀態 self.sessionHistory = data.sessions; - console.log('📊 從伺服器載入', self.sessionHistory.length, '個會話'); + console.log('📊 從伺服器載入', self.sessionHistory.length, '個實時會話狀態'); // 載入完成後進行清理和統計更新 self.cleanupExpiredSessions(); @@ -621,13 +627,53 @@ self.onDataChanged(); } } else { - console.warn('📊 伺服器回應格式錯誤:', data); - self.sessionHistory = []; + console.warn('📊 實時會話狀態回應格式錯誤,回退到歷史文件'); + self.loadFromHistoryFile(); + } + }) + .catch(function(error) { + console.warn('📊 獲取實時會話狀態失敗,回退到歷史文件:', error); + self.loadFromHistoryFile(); + }); + }; - // 即使沒有資料也要更新統計 + /** + * 從歷史文件載入會話數據(備用方案) + */ + SessionDataManager.prototype.loadFromHistoryFile = function() { + const self = this; + + fetch('/api/load-session-history') + .then(function(response) { + if (response.ok) { + return response.json(); + } else { + throw new Error('伺服器回應錯誤: ' + response.status); + } + }) + .then(function(data) { + if (data && Array.isArray(data.sessions)) { + self.sessionHistory = data.sessions; + console.log('📊 從歷史文件載入', self.sessionHistory.length, '個會話'); + + // 載入完成後進行清理和統計更新 + self.cleanupExpiredSessions(); + self.updateStats(); + + // 觸發歷史記錄變更回調 + if (self.onHistoryChange) { + self.onHistoryChange(self.sessionHistory); + } + + // 觸發資料變更回調 + if (self.onDataChanged) { + self.onDataChanged(); + } + } else { + console.warn('📊 歷史文件回應格式錯誤:', data); + self.sessionHistory = []; self.updateStats(); - // 觸發歷史記錄變更回調(空列表) if (self.onHistoryChange) { self.onHistoryChange(self.sessionHistory); } @@ -638,13 +684,10 @@ } }) .catch(function(error) { - console.warn('📊 從伺服器載入會話歷史失敗:', error); + console.warn('📊 從歷史文件載入失敗:', error); self.sessionHistory = []; - - // 載入失敗時也要更新統計 self.updateStats(); - // 觸發歷史記錄變更回調(空列表) if (self.onHistoryChange) { self.onHistoryChange(self.sessionHistory); } diff --git a/src/mcp_feedback_enhanced/web/static/js/modules/session/session-ui-renderer.js b/src/mcp_feedback_enhanced/web/static/js/modules/session/session-ui-renderer.js index abe0f72..12d5b3b 100644 --- a/src/mcp_feedback_enhanced/web/static/js/modules/session/session-ui-renderer.js +++ b/src/mcp_feedback_enhanced/web/static/js/modules/session/session-ui-renderer.js @@ -486,9 +486,18 @@ // 狀態徽章 const statusContainer = DOMUtils.createElement('div', { className: 'session-status' }); + const statusText = StatusUtils.getStatusText(sessionData.status); + + // 添加調試信息 + console.log('🎨 會話狀態調試:', { + sessionId: sessionData.session_id ? sessionData.session_id.substring(0, 8) + '...' : 'unknown', + rawStatus: sessionData.status, + displayText: statusText + }); + const statusBadge = DOMUtils.createElement('span', { className: 'status-badge ' + (sessionData.status || 'waiting'), - textContent: StatusUtils.getStatusText(sessionData.status) + textContent: statusText }); statusContainer.appendChild(statusBadge); diff --git a/src/mcp_feedback_enhanced/web/static/js/modules/utils/status-utils.js b/src/mcp_feedback_enhanced/web/static/js/modules/utils/status-utils.js index c547bd1..fc815a6 100644 --- a/src/mcp_feedback_enhanced/web/static/js/modules/utils/status-utils.js +++ b/src/mcp_feedback_enhanced/web/static/js/modules/utils/status-utils.js @@ -47,7 +47,7 @@ 'waiting_for_feedback': 'connectionMonitor.waiting', 'active': 'status.processing.title', 'feedback_submitted': 'status.submitted.title', - 'completed': 'status.submitted.title', + 'completed': 'status.completed.title', 'timeout': 'session.timeout', 'error': 'status.error', 'expired': 'session.timeout',