️ 提升效能與穩定性:引入日誌與防抖/節流機制

This commit is contained in:
Minidoracat 2025-06-15 14:51:36 +08:00
parent e28f5a1ebd
commit 59deb34f4f
13 changed files with 1241 additions and 115 deletions

View File

@ -49,9 +49,45 @@
this.isInitialized = false;
this.pendingSubmission = null;
// 初始化防抖函數
this.initDebounceHandlers();
console.log('🚀 FeedbackApp 建構函數初始化完成');
}
/**
* 初始化防抖處理器
*/
FeedbackApp.prototype.initDebounceHandlers = function() {
// 為自動提交檢查添加防抖
this._debouncedCheckAndStartAutoSubmit = window.MCPFeedback.Utils.DOM.debounce(
this._originalCheckAndStartAutoSubmit.bind(this),
200,
false
);
// 為 WebSocket 訊息處理添加防抖
this._debouncedHandleWebSocketMessage = window.MCPFeedback.Utils.DOM.debounce(
this._originalHandleWebSocketMessage.bind(this),
50,
false
);
// 為會話更新處理添加防抖
this._debouncedHandleSessionUpdated = window.MCPFeedback.Utils.DOM.debounce(
this._originalHandleSessionUpdated.bind(this),
100,
false
);
// 為狀態更新處理添加防抖
this._debouncedHandleStatusUpdate = window.MCPFeedback.Utils.DOM.debounce(
this._originalHandleStatusUpdate.bind(this),
100,
false
);
};
/**
* 初始化應用程式
*/
@ -222,7 +258,14 @@
self.checkAndStartAutoSubmit();
}, 500); // 延遲 500ms 確保所有初始化完成
// 16. 建立 WebSocket 連接
// 16. 播放啟動音效(如果音效已啟用)
setTimeout(function() {
if (self.audioManager) {
self.audioManager.playStartupNotification();
}
}, 800); // 延遲 800ms 確保所有初始化完成且避免與其他音效衝突
// 17. 建立 WebSocket 連接
self.webSocketManager.connect();
resolve();
@ -532,9 +575,9 @@
};
/**
* 處理 WebSocket 訊息
* 處理 WebSocket 訊息原始版本供防抖使用
*/
FeedbackApp.prototype.handleWebSocketMessage = function(data) {
FeedbackApp.prototype._originalHandleWebSocketMessage = function(data) {
console.log('📨 處理 WebSocket 訊息:', data);
switch (data.type) {
@ -555,11 +598,11 @@
break;
case 'status_update':
console.log('狀態更新:', data.status_info);
this.handleStatusUpdate(data.status_info);
this._originalHandleStatusUpdate(data.status_info);
break;
case 'session_updated':
console.log('🔄 收到會話更新訊息:', data.session_info);
this.handleSessionUpdated(data);
this._originalHandleSessionUpdated(data);
break;
case 'desktop_close_request':
console.log('🖥️ 收到桌面關閉請求');
@ -568,6 +611,18 @@
}
};
/**
* 處理 WebSocket 訊息防抖版本
*/
FeedbackApp.prototype.handleWebSocketMessage = function(data) {
if (this._debouncedHandleWebSocketMessage) {
this._debouncedHandleWebSocketMessage(data);
} else {
// 回退到原始方法(防抖未初始化時)
this._originalHandleWebSocketMessage(data);
}
};
/**
* 處理 WebSocket 關閉
*/
@ -629,9 +684,9 @@
};
/**
* 處理會話更新
* 處理會話更新原始版本供防抖使用
*/
FeedbackApp.prototype.handleSessionUpdated = function(data) {
FeedbackApp.prototype._originalHandleSessionUpdated = function(data) {
console.log('🔄 處理會話更新:', data.session_info);
// 播放音效通知
@ -734,9 +789,21 @@
};
/**
* 處理狀態更新
* 處理會話更新防抖版本
*/
FeedbackApp.prototype.handleStatusUpdate = function(statusInfo) {
FeedbackApp.prototype.handleSessionUpdated = function(data) {
if (this._debouncedHandleSessionUpdated) {
this._debouncedHandleSessionUpdated(data);
} else {
// 回退到原始方法(防抖未初始化時)
this._originalHandleSessionUpdated(data);
}
};
/**
* 處理狀態更新原始版本供防抖使用
*/
FeedbackApp.prototype._originalHandleStatusUpdate = function(statusInfo) {
console.log('處理狀態更新:', statusInfo);
// 更新 SessionManager 的狀態資訊
@ -784,6 +851,18 @@
}
};
/**
* 處理狀態更新防抖版本
*/
FeedbackApp.prototype.handleStatusUpdate = function(statusInfo) {
if (this._debouncedHandleStatusUpdate) {
this._debouncedHandleStatusUpdate(statusInfo);
} else {
// 回退到原始方法(防抖未初始化時)
this._originalHandleStatusUpdate(statusInfo);
}
};
/**
* 提交回饋
*/
@ -1230,10 +1309,14 @@
};
/**
* 檢查並啟動自動提交如果條件滿足
* 檢查並啟動自動提交原始版本供防抖使用
*/
FeedbackApp.prototype.checkAndStartAutoSubmit = function() {
console.log('🔍 檢查自動提交條件...');
FeedbackApp.prototype._originalCheckAndStartAutoSubmit = function() {
// 減少重複日誌:只在首次檢查或條件變化時記錄
if (!this._lastAutoSubmitCheck || Date.now() - this._lastAutoSubmitCheck > 1000) {
console.log('🔍 檢查自動提交條件...');
this._lastAutoSubmitCheck = Date.now();
}
if (!this.autoSubmitManager || !this.settingsManager || !this.promptManager) {
console.log('⚠️ 自動提交管理器、設定管理器或提示詞管理器未初始化');
@ -1288,6 +1371,18 @@
}
};
/**
* 檢查並啟動自動提交防抖版本
*/
FeedbackApp.prototype.checkAndStartAutoSubmit = function() {
if (this._debouncedCheckAndStartAutoSubmit) {
this._debouncedCheckAndStartAutoSubmit();
} else {
// 回退到原始方法(防抖未初始化時)
this._originalCheckAndStartAutoSubmit();
}
};
/**
* 處理自動提交狀態變更
*/

View File

@ -59,9 +59,18 @@
// 當前播放的 Audio 物件
this.currentAudio = null;
// 用戶互動檢測
this.userHasInteracted = false;
this.pendingNotifications = [];
this.autoplayBlocked = false;
this.interactionListenersAdded = false;
// 回調函數
this.onSettingsChange = options.onSettingsChange || null;
// 啟動音效播放標記
this.startupNotificationPlayed = false;
console.log('🔊 AudioManager 初始化完成');
}
@ -70,6 +79,7 @@
*/
AudioManager.prototype.initialize = function() {
this.loadAudioSettings();
this.setupUserInteractionDetection();
console.log('✅ AudioManager 初始化完成');
};
@ -122,7 +132,7 @@
};
/**
* 播放通知音效
* 播放通知音效智能播放策略
*/
AudioManager.prototype.playNotification = function() {
if (!this.currentAudioSettings.enabled) {
@ -134,51 +144,114 @@
const audioData = this.getAudioById(this.currentAudioSettings.selectedAudioId);
if (!audioData) {
console.warn('⚠️ 找不到指定的音效,使用預設音效');
this.playAudio(this.defaultAudios['default-beep']);
this.playAudioSmart(this.defaultAudios['default-beep']);
return;
}
this.playAudio(audioData);
this.playAudioSmart(audioData);
} catch (error) {
console.error('❌ 播放通知音效失敗:', error);
}
};
/**
* 播放指定的音效
* 播放啟動音效通知應用程式就緒時播放
*/
AudioManager.prototype.playStartupNotification = function() {
if (!this.currentAudioSettings.enabled) {
console.log('🔇 音效通知已停用,跳過啟動音效');
return;
}
// 確保啟動音效只播放一次
if (this.startupNotificationPlayed) {
console.log('🔇 啟動音效已播放過,跳過重複播放');
return;
}
this.startupNotificationPlayed = true;
console.log('🎵 播放應用程式啟動音效');
try {
const audioData = this.getAudioById(this.currentAudioSettings.selectedAudioId);
if (!audioData) {
console.warn('⚠️ 找不到指定的音效,使用預設啟動音效');
this.playAudioSmart(this.defaultAudios['default-beep']);
return;
}
this.playAudioSmart(audioData);
} catch (error) {
console.error('❌ 播放啟動音效失敗:', error);
}
};
/**
* 智能音效播放處理自動播放限制
*/
AudioManager.prototype.playAudioSmart = function(audioData) {
// 如果已知自動播放被阻止,直接加入待播放隊列
if (this.autoplayBlocked && !this.userHasInteracted) {
this.addToPendingNotifications(audioData);
return;
}
// 嘗試播放
this.playAudio(audioData)
.then(() => {
// 播放成功,清空待播放隊列
this.processPendingNotifications();
})
.catch((error) => {
if (error.name === 'NotAllowedError') {
// 自動播放被阻止
this.autoplayBlocked = true;
this.addToPendingNotifications(audioData);
this.showAutoplayBlockedNotification();
}
});
};
/**
* 播放指定的音效返回 Promise
*/
AudioManager.prototype.playAudio = function(audioData) {
try {
// 停止當前播放的音效
if (this.currentAudio) {
this.currentAudio.pause();
this.currentAudio = null;
return new Promise((resolve, reject) => {
try {
// 停止當前播放的音效
if (this.currentAudio) {
this.currentAudio.pause();
this.currentAudio = null;
}
// 建立新的 Audio 物件
this.currentAudio = new Audio();
this.currentAudio.src = 'data:' + audioData.mimeType + ';base64,' + audioData.data;
this.currentAudio.volume = this.currentAudioSettings.volume / 100;
// 播放音效
const playPromise = this.currentAudio.play();
if (playPromise !== undefined) {
playPromise
.then(() => {
console.log('🔊 音效播放成功:', audioData.name);
resolve();
})
.catch(error => {
console.error('❌ 音效播放失敗:', error);
reject(error);
});
} else {
// 舊版瀏覽器,假設播放成功
console.log('🔊 音效播放(舊版瀏覽器):', audioData.name);
resolve();
}
} catch (error) {
console.error('❌ 播放音效時發生錯誤:', error);
reject(error);
}
// 建立新的 Audio 物件
this.currentAudio = new Audio();
this.currentAudio.src = 'data:' + audioData.mimeType + ';base64,' + audioData.data;
this.currentAudio.volume = this.currentAudioSettings.volume / 100;
// 播放音效
const playPromise = this.currentAudio.play();
if (playPromise !== undefined) {
playPromise
.then(() => {
console.log('🔊 音效播放成功:', audioData.name);
})
.catch(error => {
console.error('❌ 音效播放失敗:', error);
// 可能是瀏覽器的自動播放政策限制
if (error.name === 'NotAllowedError') {
console.warn('⚠️ 瀏覽器阻止自動播放,需要用戶互動');
}
});
}
} catch (error) {
console.error('❌ 播放音效時發生錯誤:', error);
}
});
};
/**
@ -433,6 +506,97 @@
return btoa(binary);
};
/**
* 設置用戶互動檢測
*/
AudioManager.prototype.setupUserInteractionDetection = function() {
if (this.interactionListenersAdded) return;
const self = this;
const interactionEvents = ['click', 'keydown', 'touchstart'];
const handleUserInteraction = function() {
if (!self.userHasInteracted) {
self.userHasInteracted = true;
console.log('🎯 檢測到用戶互動,音效播放已解鎖');
// 播放待播放的通知
self.processPendingNotifications();
// 移除事件監聽器
interactionEvents.forEach(event => {
document.removeEventListener(event, handleUserInteraction, true);
});
self.interactionListenersAdded = false;
}
};
// 添加事件監聽器
interactionEvents.forEach(event => {
document.addEventListener(event, handleUserInteraction, true);
});
this.interactionListenersAdded = true;
console.log('🎯 用戶互動檢測已設置');
};
/**
* 添加到待播放通知隊列
*/
AudioManager.prototype.addToPendingNotifications = function(audioData) {
// 限制隊列長度,避免積累太多通知
if (this.pendingNotifications.length >= 3) {
this.pendingNotifications.shift(); // 移除最舊的通知
}
this.pendingNotifications.push({
audioData: audioData,
timestamp: Date.now()
});
console.log('📋 音效已加入待播放隊列:', audioData.name, '隊列長度:', this.pendingNotifications.length);
};
/**
* 處理待播放的通知
*/
AudioManager.prototype.processPendingNotifications = function() {
if (this.pendingNotifications.length === 0) return;
console.log('🔊 處理待播放通知,數量:', this.pendingNotifications.length);
// 只播放最新的通知,避免音效重疊
const latestNotification = this.pendingNotifications[this.pendingNotifications.length - 1];
this.pendingNotifications = []; // 清空隊列
this.playAudio(latestNotification.audioData)
.then(() => {
console.log('🔊 待播放通知播放成功');
})
.catch(error => {
console.warn('⚠️ 待播放通知播放失敗:', error);
});
};
/**
* 顯示自動播放被阻止的通知
*/
AudioManager.prototype.showAutoplayBlockedNotification = function() {
// 只顯示一次通知
if (this.autoplayNotificationShown) return;
this.autoplayNotificationShown = true;
console.log('🔇 瀏覽器阻止音效自動播放,請點擊頁面任意位置以啟用音效通知');
// 可以在這裡添加 UI 通知邏輯
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
const message = window.i18nManager ?
window.i18nManager.t('audio.autoplayBlocked', '瀏覽器阻止音效自動播放,請點擊頁面以啟用音效通知') :
'瀏覽器阻止音效自動播放,請點擊頁面以啟用音效通知';
window.MCPFeedback.Utils.showMessage(message, 'info');
}
};
/**
* 獲取當前設定
*/

View File

@ -0,0 +1,339 @@
/**
* MCP Feedback Enhanced - 日誌管理模組
* ===================================
*
* 統一的日誌管理系統支援不同等級的日誌輸出
* 生產環境可關閉詳細日誌以提升效能
*/
(function() {
'use strict';
// 確保命名空間存在
window.MCPFeedback = window.MCPFeedback || {};
/**
* 日誌等級枚舉
*/
const LogLevel = {
ERROR: 0, // 錯誤:嚴重問題,必須記錄
WARN: 1, // 警告:潛在問題,建議記錄
INFO: 2, // 資訊:一般資訊,正常記錄
DEBUG: 3, // 調試:詳細資訊,開發時記錄
TRACE: 4 // 追蹤:最詳細資訊,深度調試時記錄
};
/**
* 日誌等級名稱映射
*/
const LogLevelNames = {
[LogLevel.ERROR]: 'ERROR',
[LogLevel.WARN]: 'WARN',
[LogLevel.INFO]: 'INFO',
[LogLevel.DEBUG]: 'DEBUG',
[LogLevel.TRACE]: 'TRACE'
};
/**
* 日誌管理器
*/
function Logger(options) {
options = options || {};
// 當前日誌等級(預設為 INFO
this.currentLevel = this.parseLogLevel(options.level) || LogLevel.INFO;
// 模組名稱
this.moduleName = options.moduleName || 'App';
// 是否啟用時間戳
this.enableTimestamp = options.enableTimestamp !== false;
// 是否啟用模組名稱
this.enableModuleName = options.enableModuleName !== false;
// 是否啟用顏色(僅在支援的環境中)
this.enableColors = options.enableColors !== false;
// 自訂輸出函數
this.customOutput = options.customOutput || null;
// 日誌緩衝區(用於收集日誌)
this.logBuffer = [];
this.maxBufferSize = options.maxBufferSize || 1000;
// 顏色映射
this.colors = {
[LogLevel.ERROR]: '#f44336', // 紅色
[LogLevel.WARN]: '#ff9800', // 橙色
[LogLevel.INFO]: '#2196f3', // 藍色
[LogLevel.DEBUG]: '#4caf50', // 綠色
[LogLevel.TRACE]: '#9c27b0' // 紫色
};
}
/**
* 解析日誌等級
*/
Logger.prototype.parseLogLevel = function(level) {
if (typeof level === 'number') {
return level;
}
if (typeof level === 'string') {
const upperLevel = level.toUpperCase();
for (const [value, name] of Object.entries(LogLevelNames)) {
if (name === upperLevel) {
return parseInt(value);
}
}
}
return null;
};
/**
* 設置日誌等級
*/
Logger.prototype.setLevel = function(level) {
const parsedLevel = this.parseLogLevel(level);
if (parsedLevel !== null) {
this.currentLevel = parsedLevel;
this.info('日誌等級已設置為:', LogLevelNames[this.currentLevel]);
} else {
this.warn('無效的日誌等級:', level);
}
};
/**
* 獲取當前日誌等級
*/
Logger.prototype.getLevel = function() {
return this.currentLevel;
};
/**
* 檢查是否應該記錄指定等級的日誌
*/
Logger.prototype.shouldLog = function(level) {
return level <= this.currentLevel;
};
/**
* 格式化日誌訊息
*/
Logger.prototype.formatMessage = function(level, args) {
const parts = [];
// 添加時間戳
if (this.enableTimestamp) {
const now = new Date();
const timestamp = now.toISOString().substr(11, 12); // HH:mm:ss.SSS
parts.push(`[${timestamp}]`);
}
// 添加等級
parts.push(`[${LogLevelNames[level]}]`);
// 添加模組名稱
if (this.enableModuleName) {
parts.push(`[${this.moduleName}]`);
}
// 組合前綴
const prefix = parts.join(' ');
// 轉換參數為字符串
const messages = Array.from(args).map(arg => {
if (typeof arg === 'object') {
try {
return JSON.stringify(arg, null, 2);
} catch (e) {
return String(arg);
}
}
return String(arg);
});
return {
prefix: prefix,
message: messages.join(' '),
fullMessage: prefix + ' ' + messages.join(' ')
};
};
/**
* 輸出日誌
*/
Logger.prototype.output = function(level, formatted) {
// 添加到緩衝區
this.addToBuffer(level, formatted);
// 如果有自訂輸出函數,使用它
if (this.customOutput) {
this.customOutput(level, formatted);
return;
}
// 使用瀏覽器控制台
const consoleMethods = {
[LogLevel.ERROR]: 'error',
[LogLevel.WARN]: 'warn',
[LogLevel.INFO]: 'info',
[LogLevel.DEBUG]: 'log',
[LogLevel.TRACE]: 'log'
};
const method = consoleMethods[level] || 'log';
// 如果支援顏色且啟用
if (this.enableColors && console.log.toString().indexOf('native') === -1) {
const color = this.colors[level];
console[method](`%c${formatted.fullMessage}`, `color: ${color}`);
} else {
console[method](formatted.fullMessage);
}
};
/**
* 添加到日誌緩衝區
*/
Logger.prototype.addToBuffer = function(level, formatted) {
const logEntry = {
timestamp: Date.now(),
level: level,
levelName: LogLevelNames[level],
moduleName: this.moduleName,
message: formatted.message,
fullMessage: formatted.fullMessage
};
this.logBuffer.push(logEntry);
// 限制緩衝區大小
if (this.logBuffer.length > this.maxBufferSize) {
this.logBuffer.shift();
}
};
/**
* 通用日誌方法
*/
Logger.prototype.log = function(level) {
if (!this.shouldLog(level)) {
return;
}
const args = Array.prototype.slice.call(arguments, 1);
const formatted = this.formatMessage(level, args);
this.output(level, formatted);
};
/**
* 錯誤日誌
*/
Logger.prototype.error = function() {
this.log.apply(this, [LogLevel.ERROR].concat(Array.prototype.slice.call(arguments)));
};
/**
* 警告日誌
*/
Logger.prototype.warn = function() {
this.log.apply(this, [LogLevel.WARN].concat(Array.prototype.slice.call(arguments)));
};
/**
* 資訊日誌
*/
Logger.prototype.info = function() {
this.log.apply(this, [LogLevel.INFO].concat(Array.prototype.slice.call(arguments)));
};
/**
* 調試日誌
*/
Logger.prototype.debug = function() {
this.log.apply(this, [LogLevel.DEBUG].concat(Array.prototype.slice.call(arguments)));
};
/**
* 追蹤日誌
*/
Logger.prototype.trace = function() {
this.log.apply(this, [LogLevel.TRACE].concat(Array.prototype.slice.call(arguments)));
};
/**
* 獲取日誌緩衝區
*/
Logger.prototype.getBuffer = function() {
return this.logBuffer.slice(); // 返回副本
};
/**
* 清空日誌緩衝區
*/
Logger.prototype.clearBuffer = function() {
this.logBuffer = [];
};
/**
* 導出日誌
*/
Logger.prototype.exportLogs = function(options) {
options = options || {};
const format = options.format || 'json';
const minLevel = this.parseLogLevel(options.minLevel) || LogLevel.ERROR;
const filteredLogs = this.logBuffer.filter(log => log.level <= minLevel);
if (format === 'json') {
return JSON.stringify(filteredLogs, null, 2);
} else if (format === 'text') {
return filteredLogs.map(log => log.fullMessage).join('\n');
}
return filteredLogs;
};
// 全域日誌管理器
const globalLogger = new Logger({
moduleName: 'Global',
level: LogLevel.INFO
});
// 從環境變數或 URL 參數檢測日誌等級
function detectLogLevel() {
// 檢查 URL 參數
const urlParams = new URLSearchParams(window.location.search);
const urlLogLevel = urlParams.get('logLevel') || urlParams.get('log_level');
if (urlLogLevel) {
return urlLogLevel;
}
// 檢查 localStorage
const storedLevel = localStorage.getItem('mcp-log-level');
if (storedLevel) {
return storedLevel;
}
// 檢查是否為開發環境
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
return LogLevel.DEBUG;
}
return LogLevel.INFO;
}
// 設置全域日誌等級
globalLogger.setLevel(detectLogLevel());
// 匯出到全域命名空間
window.MCPFeedback.Logger = Logger;
window.MCPFeedback.LogLevel = LogLevel;
window.MCPFeedback.logger = globalLogger;
console.log('✅ Logger 模組載入完成,當前等級:', LogLevelNames[globalLogger.getLevel()]);
})();

View File

@ -63,6 +63,9 @@
showFullSessionId: options.showFullSessionId || false
});
// 初始化防抖處理器
this.initDebounceHandlers();
// 最後初始化數據管理器(確保 UI 組件已準備好接收回調)
this.dataManager = new window.MCPFeedback.Session.DataManager({
settingsManager: this.settingsManager,
@ -81,13 +84,49 @@
});
};
/**
* 初始化防抖處理器
*/
SessionManager.prototype.initDebounceHandlers = function() {
// 為會話變更處理添加防抖
this._debouncedHandleSessionChange = window.MCPFeedback.Utils.DOM.debounce(
this._originalHandleSessionChange.bind(this),
100,
false
);
// 為歷史記錄變更處理添加防抖
this._debouncedHandleHistoryChange = window.MCPFeedback.Utils.DOM.debounce(
this._originalHandleHistoryChange.bind(this),
150,
false
);
// 為統計資訊變更處理添加防抖
this._debouncedHandleStatsChange = window.MCPFeedback.Utils.DOM.debounce(
this._originalHandleStatsChange.bind(this),
100,
false
);
// 為資料變更處理添加防抖
this._debouncedHandleDataChanged = window.MCPFeedback.Utils.DOM.debounce(
this._originalHandleDataChanged.bind(this),
200,
false
);
};
/**
* 處理會話變更
* 處理會話變更原始版本供防抖使用
*/
SessionManager.prototype.handleSessionChange = function(sessionData) {
console.log('📋 處理會話變更:', sessionData);
SessionManager.prototype._originalHandleSessionChange = function(sessionData) {
// 減少重複日誌:只在會話 ID 變化時記錄
const sessionId = sessionData ? sessionData.session_id : null;
if (!this._lastSessionId || this._lastSessionId !== sessionId) {
console.log('📋 處理會話變更:', sessionData);
this._lastSessionId = sessionId;
}
// 更新 UI 渲染
this.uiRenderer.renderCurrentSession(sessionData);
@ -99,29 +138,74 @@
};
/**
* 處理歷史記錄變更
* 處理會話變更防抖版本
*/
SessionManager.prototype.handleHistoryChange = function(history) {
console.log('📋 處理歷史記錄變更:', history.length, '個會話');
SessionManager.prototype.handleSessionChange = function(sessionData) {
if (this._debouncedHandleSessionChange) {
this._debouncedHandleSessionChange(sessionData);
} else {
// 回退到原始方法(防抖未初始化時)
this._originalHandleSessionChange(sessionData);
}
};
/**
* 處理歷史記錄變更原始版本供防抖使用
*/
SessionManager.prototype._originalHandleHistoryChange = function(history) {
// 減少重複日誌:只在歷史記錄數量變化時記錄
if (!this._lastHistoryCount || this._lastHistoryCount !== history.length) {
console.log('📋 處理歷史記錄變更:', history.length, '個會話');
this._lastHistoryCount = history.length;
}
// 更新 UI 渲染
this.uiRenderer.renderSessionHistory(history);
};
/**
* 處理統計資訊變更
* 處理歷史記錄變更防抖版本
*/
SessionManager.prototype.handleStatsChange = function(stats) {
console.log('📋 處理統計資訊變更:', stats);
SessionManager.prototype.handleHistoryChange = function(history) {
if (this._debouncedHandleHistoryChange) {
this._debouncedHandleHistoryChange(history);
} else {
// 回退到原始方法(防抖未初始化時)
this._originalHandleHistoryChange(history);
}
};
/**
* 處理統計資訊變更原始版本供防抖使用
*/
SessionManager.prototype._originalHandleStatsChange = function(stats) {
// 減少重複日誌:只在統計資訊有意義變化時記錄
const statsKey = stats ? JSON.stringify(stats) : null;
if (!this._lastStatsKey || this._lastStatsKey !== statsKey) {
console.log('📋 處理統計資訊變更:', stats);
this._lastStatsKey = statsKey;
}
// 更新 UI 渲染
this.uiRenderer.renderStats(stats);
};
/**
* 處理資料變更用於異步載入完成後的更新
* 處理統計資訊變更防抖版本
*/
SessionManager.prototype.handleDataChanged = function() {
SessionManager.prototype.handleStatsChange = function(stats) {
if (this._debouncedHandleStatsChange) {
this._debouncedHandleStatsChange(stats);
} else {
// 回退到原始方法(防抖未初始化時)
this._originalHandleStatsChange(stats);
}
};
/**
* 處理資料變更原始版本供防抖使用
*/
SessionManager.prototype._originalHandleDataChanged = function() {
console.log('📋 處理資料變更,重新渲染所有內容');
// 重新渲染所有內容
@ -134,6 +218,18 @@
this.uiRenderer.renderStats(stats);
};
/**
* 處理資料變更防抖版本
*/
SessionManager.prototype.handleDataChanged = function() {
if (this._debouncedHandleDataChanged) {
this._debouncedHandleDataChanged();
} else {
// 回退到原始方法(防抖未初始化時)
this._originalHandleDataChanged();
}
};
/**
* 設置事件監聽器
*/

View File

@ -14,6 +14,11 @@
const DOMUtils = window.MCPFeedback.Utils.DOM;
const TimeUtils = window.MCPFeedback.Utils.Time;
// 創建模組專用日誌器
const logger = window.MCPFeedback.Logger ?
new window.MCPFeedback.Logger({ moduleName: 'SessionUIRenderer' }) :
console;
const StatusUtils = window.MCPFeedback.Utils.Status;
/**
@ -35,11 +40,26 @@
this.activeTimeTimer = null;
this.currentSessionData = null;
// 渲染防抖機制
this.renderDebounceTimers = {
stats: null,
history: null,
currentSession: null
};
this.renderDebounceDelay = options.renderDebounceDelay || 100; // 預設 100ms 防抖延遲
// 快取上次渲染的數據,避免不必要的重渲染
this.lastRenderedData = {
stats: null,
historyLength: 0,
currentSessionId: null
};
this.initializeElements();
this.initializeProjectPathDisplay();
this.startActiveTimeTimer();
console.log('🎨 SessionUIRenderer 初始化完成');
logger.info('SessionUIRenderer 初始化完成,渲染防抖延遲:', this.renderDebounceDelay + 'ms');
}
/**
@ -109,18 +129,49 @@
};
/**
* 渲染當前會話
* 渲染當前會話帶防抖機制
*/
SessionUIRenderer.prototype.renderCurrentSession = function(sessionData) {
if (!this.currentSessionCard || !sessionData) return;
console.log('🎨 渲染當前會話:', sessionData);
const self = this;
// 檢查是否是新會話(會話 ID 變更)
const isNewSession = !this.currentSessionData ||
this.currentSessionData.session_id !== sessionData.session_id;
// 更新當前會話數據
// 檢查數據是否有變化
if (!isNewSession && self.lastRenderedData.currentSessionId === sessionData.session_id &&
self.currentSessionData &&
self.currentSessionData.status === sessionData.status &&
self.currentSessionData.summary === sessionData.summary) {
// 數據沒有重要變化,跳過渲染
return;
}
// 清除之前的防抖定時器
if (self.renderDebounceTimers.currentSession) {
clearTimeout(self.renderDebounceTimers.currentSession);
}
// 對於新會話,立即渲染;對於更新,使用防抖
if (isNewSession) {
self._performCurrentSessionRender(sessionData, isNewSession);
} else {
self.renderDebounceTimers.currentSession = setTimeout(function() {
self._performCurrentSessionRender(sessionData, false);
}, self.renderDebounceDelay);
}
};
/**
* 執行實際的當前會話渲染
*/
SessionUIRenderer.prototype._performCurrentSessionRender = function(sessionData, isNewSession) {
console.log('🎨 渲染當前會話:', sessionData);
// 更新快取
this.lastRenderedData.currentSessionId = sessionData.session_id;
this.currentSessionData = sessionData;
// 如果是新會話,重置活躍時間定時器
@ -334,13 +385,39 @@
};
/**
* 渲染會話歷史列表
* 渲染會話歷史列表帶防抖機制
*/
SessionUIRenderer.prototype.renderSessionHistory = function(sessionHistory) {
if (!this.historyList) return;
if (!this.historyList || !sessionHistory) return;
const self = this;
// 檢查數據是否有變化(簡單比較長度)
if (self.lastRenderedData.historyLength === sessionHistory.length) {
// 長度沒有變化,跳過渲染(可以進一步優化為深度比較)
return;
}
// 清除之前的防抖定時器
if (self.renderDebounceTimers.history) {
clearTimeout(self.renderDebounceTimers.history);
}
// 設置新的防抖定時器
self.renderDebounceTimers.history = setTimeout(function() {
self._performHistoryRender(sessionHistory);
}, self.renderDebounceDelay);
};
/**
* 執行實際的會話歷史渲染
*/
SessionUIRenderer.prototype._performHistoryRender = function(sessionHistory) {
console.log('🎨 渲染會話歷史:', sessionHistory.length, '個會話');
// 更新快取
this.lastRenderedData.historyLength = sessionHistory.length;
// 清空現有內容
DOMUtils.clearElement(this.historyList);
@ -519,30 +596,59 @@
};
/**
* 渲染統計資訊
* 渲染統計資訊帶防抖機制
*/
SessionUIRenderer.prototype.renderStats = function(stats) {
console.log('🎨 渲染統計資訊:', stats);
console.log('🎨 統計元素狀態:', {
todayCount: !!this.statsElements.todayCount,
averageDuration: !!this.statsElements.averageDuration
});
if (!stats) return;
const self = this;
// 檢查數據是否有變化
if (self.lastRenderedData.stats &&
self.lastRenderedData.stats.todayCount === stats.todayCount &&
self.lastRenderedData.stats.averageDuration === stats.averageDuration) {
// 數據沒有變化,跳過渲染
return;
}
// 清除之前的防抖定時器
if (self.renderDebounceTimers.stats) {
clearTimeout(self.renderDebounceTimers.stats);
}
// 設置新的防抖定時器
self.renderDebounceTimers.stats = setTimeout(function() {
self._performStatsRender(stats);
}, self.renderDebounceDelay);
};
/**
* 執行實際的統計資訊渲染
*/
SessionUIRenderer.prototype._performStatsRender = function(stats) {
logger.debug('渲染統計資訊:', stats);
// 更新快取
this.lastRenderedData.stats = {
todayCount: stats.todayCount,
averageDuration: stats.averageDuration
};
// 更新今日會話數
if (this.statsElements.todayCount) {
DOMUtils.safeSetTextContent(this.statsElements.todayCount, stats.todayCount.toString());
console.log('🎨 已更新今日會話數:', stats.todayCount);
logger.debug('已更新今日會話數:', stats.todayCount);
} else {
console.warn('🎨 找不到今日會話數元素 (.stat-today-count)');
logger.warn('找不到今日會話數元素 (.stat-today-count)');
}
// 更新今日平均時長
if (this.statsElements.averageDuration) {
const durationText = TimeUtils.formatDuration(stats.averageDuration);
DOMUtils.safeSetTextContent(this.statsElements.averageDuration, durationText);
console.log('🎨 已更新今日平均時長:', durationText);
logger.debug('已更新今日平均時長:', durationText);
} else {
console.warn('🎨 找不到平均時長元素 (.stat-average-duration)');
logger.warn('找不到平均時長元素 (.stat-average-duration)');
}
};
@ -624,11 +730,24 @@
// 停止定時器
this.stopActiveTimeTimer();
// 清理防抖定時器
Object.keys(this.renderDebounceTimers).forEach(key => {
if (this.renderDebounceTimers[key]) {
clearTimeout(this.renderDebounceTimers[key]);
this.renderDebounceTimers[key] = null;
}
});
// 清理引用
this.currentSessionCard = null;
this.historyList = null;
this.statsElements = {};
this.currentSessionData = null;
this.lastRenderedData = {
stats: null,
historyLength: 0,
currentSessionId: null
};
console.log('🎨 SessionUIRenderer 清理完成');
};

View File

@ -12,6 +12,11 @@
window.MCPFeedback = window.MCPFeedback || {};
const Utils = window.MCPFeedback.Utils;
// 創建模組專用日誌器
const logger = window.MCPFeedback.Logger ?
new window.MCPFeedback.Logger({ moduleName: 'SettingsManager' }) :
console;
/**
* 設定管理器建構函數
*/
@ -52,6 +57,13 @@
this.onSettingsChange = options.onSettingsChange || null;
this.onLanguageChange = options.onLanguageChange || null;
this.onAutoSubmitStateChange = options.onAutoSubmitStateChange || null;
// 防抖機制相關
this.saveToServerDebounceTimer = null;
this.saveToServerDebounceDelay = options.saveDebounceDelay || 500; // 預設 500ms 防抖延遲
this.pendingServerSave = false;
console.log('✅ SettingsManager 建構函數初始化完成,防抖延遲:', this.saveToServerDebounceDelay + 'ms');
}
/**
@ -61,14 +73,14 @@
const self = this;
return new Promise(function(resolve, reject) {
console.log('開始載入設定...');
logger.info('開始載入設定...');
// 優先從伺服器端載入設定
self.loadFromServer()
.then(function(serverSettings) {
if (serverSettings && Object.keys(serverSettings).length > 0) {
self.currentSettings = self.mergeSettings(self.defaultSettings, serverSettings);
console.log('從伺服器端載入設定成功:', self.currentSettings);
logger.info('從伺服器端載入設定成功:', self.currentSettings);
// 同步到 localStorage
self.saveToLocalStorage();
@ -143,7 +155,7 @@
this.currentSettings = this.mergeSettings(this.currentSettings, newSettings);
}
console.log('保存設定:', this.currentSettings);
logger.debug('保存設定:', this.currentSettings);
// 保存到 localStorage
this.saveToLocalStorage();
@ -175,15 +187,43 @@
};
/**
* 保存到伺服器
* 保存到伺服器帶防抖機制
*/
SettingsManager.prototype.saveToServer = function() {
const self = this;
// 清除之前的定時器
if (self.saveToServerDebounceTimer) {
clearTimeout(self.saveToServerDebounceTimer);
}
// 標記有待處理的保存操作
self.pendingServerSave = true;
// 設置新的防抖定時器
self.saveToServerDebounceTimer = setTimeout(function() {
self._performServerSave();
}, self.saveToServerDebounceDelay);
};
/**
* 執行實際的伺服器保存操作
*/
SettingsManager.prototype._performServerSave = function() {
const self = this;
if (!self.pendingServerSave) {
return;
}
self.pendingServerSave = false;
fetch('/api/save-settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(this.currentSettings)
body: JSON.stringify(self.currentSettings)
})
.then(function(response) {
if (response.ok) {
@ -197,6 +237,21 @@
});
};
/**
* 立即保存到伺服器跳過防抖機制
* 用於重要操作如語言變更重置設定等
*/
SettingsManager.prototype.saveToServerImmediate = function() {
// 清除防抖定時器
if (this.saveToServerDebounceTimer) {
clearTimeout(this.saveToServerDebounceTimer);
this.saveToServerDebounceTimer = null;
}
// 立即執行保存
this._performServerSave();
};
/**
* 合併設定
*/
@ -232,9 +287,19 @@
// 特殊處理語言變更
if (key === 'language' && oldValue !== value) {
this.handleLanguageChange(value);
// 語言變更是重要操作,立即保存
this.saveToLocalStorage();
this.saveToServerImmediate();
// 觸發回調
if (this.onSettingsChange) {
this.onSettingsChange(this.currentSettings);
}
} else {
// 一般設定變更使用防抖保存
this.saveSettings();
}
this.saveSettings();
return this;
};
@ -304,8 +369,14 @@
// 重置為預設值
this.currentSettings = Utils.deepClone(this.defaultSettings);
// 保存重置後的設定
this.saveSettings();
// 立即保存重置後的設定(重要操作)
this.saveToLocalStorage();
this.saveToServerImmediate();
// 觸發回調
if (this.onSettingsChange) {
this.onSettingsChange(this.currentSettings);
}
return this.currentSettings;
};

View File

@ -36,9 +36,31 @@
this.onTabChange = options.onTabChange || null;
this.onLayoutModeChange = options.onLayoutModeChange || null;
// 初始化防抖函數
this.initDebounceHandlers();
this.initUIElements();
}
/**
* 初始化防抖處理器
*/
UIManager.prototype.initDebounceHandlers = function() {
// 為狀態指示器更新添加防抖
this._debouncedUpdateStatusIndicator = Utils.DOM.debounce(
this._originalUpdateStatusIndicator.bind(this),
100,
false
);
// 為狀態指示器元素更新添加防抖
this._debouncedUpdateStatusIndicatorElement = Utils.DOM.debounce(
this._originalUpdateStatusIndicatorElement.bind(this),
50,
false
);
};
/**
* 初始化 UI 元素
*/
@ -266,23 +288,39 @@
};
/**
* 更新狀態指示器
* 更新狀態指示器原始版本供防抖使用
*/
UIManager.prototype.updateStatusIndicator = function() {
UIManager.prototype._originalUpdateStatusIndicator = function() {
const feedbackStatusIndicator = Utils.safeQuerySelector('#feedbackStatusIndicator');
const combinedStatusIndicator = Utils.safeQuerySelector('#combinedFeedbackStatusIndicator');
const statusInfo = this.getStatusInfo();
if (feedbackStatusIndicator) {
this.updateStatusIndicatorElement(feedbackStatusIndicator, statusInfo);
this._originalUpdateStatusIndicatorElement(feedbackStatusIndicator, statusInfo);
}
if (combinedStatusIndicator) {
this.updateStatusIndicatorElement(combinedStatusIndicator, statusInfo);
this._originalUpdateStatusIndicatorElement(combinedStatusIndicator, statusInfo);
}
console.log('✅ 狀態指示器已更新: ' + statusInfo.status + ' - ' + statusInfo.title);
// 減少重複日誌:只在狀態真正改變時記錄
if (!this._lastStatusInfo || this._lastStatusInfo.status !== statusInfo.status) {
console.log('✅ 狀態指示器已更新: ' + statusInfo.status + ' - ' + statusInfo.title);
this._lastStatusInfo = statusInfo;
}
};
/**
* 更新狀態指示器防抖版本
*/
UIManager.prototype.updateStatusIndicator = function() {
if (this._debouncedUpdateStatusIndicator) {
this._debouncedUpdateStatusIndicator();
} else {
// 回退到原始方法(防抖未初始化時)
this._originalUpdateStatusIndicator();
}
};
/**
@ -329,9 +367,9 @@
};
/**
* 更新單個狀態指示器元素
* 更新單個狀態指示器元素原始版本供防抖使用
*/
UIManager.prototype.updateStatusIndicatorElement = function(element, statusInfo) {
UIManager.prototype._originalUpdateStatusIndicatorElement = function(element, statusInfo) {
if (!element) return;
// 更新狀態類別
@ -350,7 +388,22 @@
messageElement.textContent = statusInfo.message;
}
console.log('🔧 已更新狀態指示器: ' + element.id + ' -> ' + statusInfo.status);
// 減少重複日誌:只記錄元素 ID 變化
if (element.id) {
console.log('🔧 已更新狀態指示器: ' + element.id + ' -> ' + statusInfo.status);
}
};
/**
* 更新單個狀態指示器元素防抖版本
*/
UIManager.prototype.updateStatusIndicatorElement = function(element, statusInfo) {
if (this._debouncedUpdateStatusIndicatorElement) {
this._debouncedUpdateStatusIndicatorElement(element, statusInfo);
} else {
// 回退到原始方法(防抖未初始化時)
this._originalUpdateStatusIndicatorElement(element, statusInfo);
}
};
/**

View File

@ -353,12 +353,12 @@
FEEDBACK_SUBMITTED: 'feedback_submitted',
FEEDBACK_PROCESSING: 'processing',
// 預設設定
DEFAULT_HEARTBEAT_FREQUENCY: 30000,
DEFAULT_TAB_HEARTBEAT_FREQUENCY: 5000,
// 預設設定(優化後的值)
DEFAULT_HEARTBEAT_FREQUENCY: 60000, // 從 30 秒調整為 60 秒,減少網路負載
DEFAULT_TAB_HEARTBEAT_FREQUENCY: 10000, // 從 5 秒調整為 10 秒,減少標籤頁檢查頻率
DEFAULT_RECONNECT_DELAY: 1000,
MAX_RECONNECT_ATTEMPTS: 5,
TAB_EXPIRED_THRESHOLD: 30000,
TAB_EXPIRED_THRESHOLD: 60000, // 從 30 秒調整為 60 秒,與心跳頻率保持一致
// 訊息類型
MESSAGE_SUCCESS: 'success',

View File

@ -291,11 +291,101 @@
return true;
}
return false;
},
/**
* 防抖函數 - 延遲執行在指定時間內重複調用會重置計時器
* @param {Function} func - 要防抖的函數
* @param {number} delay - 延遲時間毫秒
* @param {boolean} immediate - 是否立即執行第一次調用
* @returns {Function} 防抖後的函數
*/
debounce: function(func, delay, immediate) {
let timeoutId;
return function() {
const context = this;
const args = arguments;
const later = function() {
timeoutId = null;
if (!immediate) {
func.apply(context, args);
}
};
const callNow = immediate && !timeoutId;
clearTimeout(timeoutId);
timeoutId = setTimeout(later, delay);
if (callNow) {
func.apply(context, args);
}
};
},
/**
* 節流函數 - 限制函數執行頻率在指定時間內最多執行一次
* @param {Function} func - 要節流的函數
* @param {number} limit - 時間間隔毫秒
* @returns {Function} 節流後的函數
*/
throttle: function(func, limit) {
let inThrottle;
return function() {
const context = this;
const args = arguments;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(function() {
inThrottle = false;
}, limit);
}
};
},
/**
* 創建帶有防抖的函數包裝器
* @param {Object} target - 目標對象
* @param {string} methodName - 方法名稱
* @param {number} delay - 防抖延遲時間
* @param {boolean} immediate - 是否立即執行
* @returns {Function} 原始函數的引用
*/
wrapWithDebounce: function(target, methodName, delay, immediate) {
if (!target || typeof target[methodName] !== 'function') {
console.warn('無法為不存在的方法添加防抖:', methodName);
return null;
}
const originalMethod = target[methodName];
target[methodName] = this.debounce(originalMethod.bind(target), delay, immediate);
return originalMethod;
},
/**
* 創建帶有節流的函數包裝器
* @param {Object} target - 目標對象
* @param {string} methodName - 方法名稱
* @param {number} limit - 節流時間間隔
* @returns {Function} 原始函數的引用
*/
wrapWithThrottle: function(target, methodName, limit) {
if (!target || typeof target[methodName] !== 'function') {
console.warn('無法為不存在的方法添加節流:', methodName);
return null;
}
const originalMethod = target[methodName];
target[methodName] = this.throttle(originalMethod.bind(target), limit);
return originalMethod;
}
};
// 將 DOMUtils 加入命名空間
window.MCPFeedback.Utils.DOM = DOMUtils;
window.MCPFeedback.DOMUtils = DOMUtils;
window.MCPFeedback.Utils.DOM = DOMUtils; // 保持向後相容
console.log('✅ DOMUtils 模組載入完成');

View File

@ -395,7 +395,8 @@
};
// 將 StatusUtils 加入命名空間
window.MCPFeedback.Utils.Status = StatusUtils;
window.MCPFeedback.StatusUtils = StatusUtils;
window.MCPFeedback.Utils.Status = StatusUtils; // 保持向後相容
console.log('✅ StatusUtils 模組載入完成');

View File

@ -432,7 +432,8 @@
};
// 將 TimeUtils 加入命名空間
window.MCPFeedback.Utils.Time = TimeUtils;
window.MCPFeedback.TimeUtils = TimeUtils;
window.MCPFeedback.Utils.Time = TimeUtils; // 保持向後相容
console.log('✅ TimeUtils 模組載入完成');

View File

@ -43,6 +43,10 @@
// 待處理的提交
this.pendingSubmission = null;
this.sessionUpdatePending = false;
// 網路狀態檢測
this.networkOnline = navigator.onLine;
this.setupNetworkStatusDetection();
}
/**
@ -217,11 +221,17 @@
self.connect();
}, 200);
}
// 只有在非正常關閉時才重連
else if (event.code !== 1000 && this.reconnectAttempts < this.maxReconnectAttempts) {
// 檢查是否應該重連
else if (this.shouldAttemptReconnect(event)) {
this.reconnectAttempts++;
this.reconnectDelay = Math.min(this.reconnectDelay * 1.5, 15000);
console.log(this.reconnectDelay / 1000 + '秒後嘗試重連... (第' + this.reconnectAttempts + '次)');
// 改進的指數退避算法:基礎延遲 * 2^重試次數,加上隨機抖動
const baseDelay = Utils.CONSTANTS.DEFAULT_RECONNECT_DELAY;
const exponentialDelay = baseDelay * Math.pow(2, this.reconnectAttempts - 1);
const jitter = Math.random() * 1000; // 0-1秒的隨機抖動
this.reconnectDelay = Math.min(exponentialDelay + jitter, 30000); // 最大 30 秒
console.log(Math.round(this.reconnectDelay / 1000) + '秒後嘗試重連... (第' + this.reconnectAttempts + '次)');
// 更新狀態為重連中
const reconnectingTemplate = window.i18nManager ? window.i18nManager.t('connectionMonitor.reconnecting') : '重連中... (第{attempt}次)';
@ -377,6 +387,64 @@
return this.isConnected && this.connectionReady;
};
/**
* 設置網路狀態檢測
*/
WebSocketManager.prototype.setupNetworkStatusDetection = function() {
const self = this;
// 監聽網路狀態變化
window.addEventListener('online', function() {
console.log('🌐 網路已恢復,嘗試重新連接...');
self.networkOnline = true;
// 如果 WebSocket 未連接且不在重連過程中,立即嘗試連接
if (!self.isConnected && self.reconnectAttempts < self.maxReconnectAttempts) {
// 重置重連計數器,因為網路問題已解決
self.reconnectAttempts = 0;
self.reconnectDelay = Utils.CONSTANTS.DEFAULT_RECONNECT_DELAY;
setTimeout(function() {
self.connect();
}, 1000); // 延遲 1 秒確保網路穩定
}
});
window.addEventListener('offline', function() {
console.log('🌐 網路已斷開');
self.networkOnline = false;
// 更新連接狀態
const offlineMessage = window.i18nManager ?
window.i18nManager.t('connectionMonitor.offline', '網路已斷開') :
'網路已斷開';
self.updateConnectionStatus('offline', offlineMessage);
});
};
/**
* 檢查是否應該嘗試重連
*/
WebSocketManager.prototype.shouldAttemptReconnect = function(event) {
// 如果網路離線,不嘗試重連
if (!this.networkOnline) {
console.log('🌐 網路離線,跳過重連');
return false;
}
// 如果是正常關閉,不重連
if (event.code === 1000) {
return false;
}
// 如果達到最大重連次數,不重連
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
return false;
}
return true;
};
/**
* 關閉連接
*/

View File

@ -1098,6 +1098,9 @@
<!-- WebSocket 和 JavaScript -->
<script src="/static/js/i18n.js?v=2025010510"></script>
<!-- 載入所有模組 -->
<!-- 核心模組(最先載入) -->
<script src="/static/js/modules/logger.js?v=2025010510"></script>
<!-- 工具模組 -->
<script src="/static/js/modules/utils/dom-utils.js?v=2025010510"></script>
<script src="/static/js/modules/utils/time-utils.js?v=2025010510"></script>
@ -1137,27 +1140,52 @@
async function initializeApp() {
const sessionId = '{{ session_id }}';
// 檢查所有必要的模組是否已載入
if (!window.MCPFeedback ||
!window.MCPFeedback.Utils ||
!window.MCPFeedback.ConnectionMonitor ||
!window.MCPFeedback.SessionManager ||
!window.MCPFeedback.FeedbackApp) {
console.error('❌ 模組載入不完整,延遲初始化...');
// 檢查核心依賴
const requiredModules = [
'MCPFeedback',
'MCPFeedback.Logger',
'MCPFeedback.Utils',
'MCPFeedback.DOMUtils',
'MCPFeedback.TimeUtils',
'MCPFeedback.StatusUtils',
'MCPFeedback.ConnectionMonitor',
'MCPFeedback.SessionManager',
'MCPFeedback.FeedbackApp'
];
const missingModules = requiredModules.filter(modulePath => {
const parts = modulePath.split('.');
let current = window;
for (const part of parts) {
if (!current[part]) return true;
current = current[part];
}
return false;
});
if (missingModules.length > 0) {
const logger = window.MCPFeedback?.logger || console;
logger.warn('模組載入不完整,缺少:', missingModules.join(', '));
setTimeout(initializeApp, 100);
return;
}
try {
const logger = window.MCPFeedback.logger;
logger.info('開始初始化應用程式...');
// 確保 I18nManager 已經初始化
if (window.i18nManager) {
logger.debug('初始化國際化管理器...');
await window.i18nManager.init();
}
// 初始化 FeedbackApp使用新的命名空間
logger.debug('創建 FeedbackApp 實例...');
window.feedbackApp = new window.MCPFeedback.FeedbackApp(sessionId);
// 初始化應用程式
logger.debug('初始化 FeedbackApp...');
await window.feedbackApp.init();
// 設置全域引用,讓 SessionManager 可以被 HTML 中的 onclick 調用
@ -1165,9 +1193,10 @@
window.MCPFeedback.app = window.feedbackApp;
}
console.log('✅ 應用程式初始化完成');
logger.info('應用程式初始化完成');
} catch (error) {
console.error('❌ 應用程式初始化失敗:', error);
const logger = window.MCPFeedback?.logger || console;
logger.error('應用程式初始化失敗:', error);
}
}