新增會話歷史保存本地和匯出功能

This commit is contained in:
Minidoracat 2025-06-14 09:29:03 +08:00
parent c254602531
commit f9df4386f8
9 changed files with 544 additions and 6 deletions

View File

@ -224,6 +224,28 @@
"unknown": "Unknown"
}
},
"sessionHistory": {
"management": {
"title": "Session History Management",
"retentionPeriod": "Retention Period",
"retentionHours": "hours",
"export": "Export",
"clear": "Clear",
"exportAll": "Export All",
"exportSingle": "Export This Session",
"confirmClear": "Are you sure you want to clear all session history?",
"exportSuccess": "Session history exported successfully",
"clearSuccess": "Session history cleared successfully",
"description": "Manage locally stored session history records, including retention period settings and data export functionality"
},
"retention": {
"24hours": "24 hours",
"72hours": "72 hours",
"168hours": "7 days",
"720hours": "30 days",
"custom": "Custom"
}
},
"connectionMonitor": {
"connecting": "Connecting...",
"connected": "Connected",

View File

@ -224,6 +224,28 @@
"unknown": "未知"
}
},
"sessionHistory": {
"management": {
"title": "会话历史管理",
"retentionPeriod": "保存期限",
"retentionHours": "小时",
"export": "导出",
"clear": "清空",
"exportAll": "导出全部",
"exportSingle": "导出此会话",
"confirmClear": "确定要清空所有会话历史吗?",
"exportSuccess": "会话历史已导出",
"clearSuccess": "会话历史已清空",
"description": "管理本地存储的会话历史记录,包括保存期限设定和数据导出功能"
},
"retention": {
"24hours": "24 小时",
"72hours": "72 小时",
"168hours": "7 天",
"720hours": "30 天",
"custom": "自定义"
}
},
"connectionMonitor": {
"connecting": "连接中...",
"connected": "已连接",

View File

@ -229,6 +229,28 @@
},
"noSummary": "無摘要"
},
"sessionHistory": {
"management": {
"title": "會話歷史管理",
"retentionPeriod": "保存期限",
"retentionHours": "小時",
"export": "匯出",
"clear": "清空",
"exportAll": "匯出全部",
"exportSingle": "匯出此會話",
"confirmClear": "確定要清空所有會話歷史嗎?",
"exportSuccess": "會話歷史已匯出",
"clearSuccess": "會話歷史已清空",
"description": "管理本地儲存的會話歷史記錄,包括保存期限設定和資料匯出功能"
},
"retention": {
"24hours": "24 小時",
"72hours": "72 小時",
"168hours": "7 天",
"720hours": "30 天",
"custom": "自訂"
}
},
"connectionMonitor": {
"connecting": "連接中...",
"connected": "已連接",

View File

@ -539,6 +539,40 @@
border-color: var(--accent-color);
}
/* 匯出按鈕樣式 */
.btn-export {
background: var(--bg-primary) !important;
border-color: var(--success-color) !important;
color: var(--success-color) !important;
}
.btn-export:hover {
background: var(--success-color) !important;
color: white !important;
border-color: var(--success-color) !important;
}
/* 會話歷史管理按鈕樣式 */
#exportSessionHistoryBtn {
background: var(--success-color);
border-color: var(--success-color);
color: white;
}
#exportSessionHistoryBtn:hover {
background: var(--success-color-dark, #28a745);
border-color: var(--success-color-dark, #28a745);
}
#clearSessionHistoryBtn {
background: var(--bg-primary);
}
#clearSessionHistoryBtn:hover {
background: var(--error-color);
color: white;
}
/* ===== 會話統計 ===== */
.session-stats-section {
margin-top: 16px;

View File

@ -73,6 +73,7 @@
// 最後初始化數據管理器(確保 UI 組件已準備好接收回調)
this.dataManager = new window.MCPFeedback.Session.DataManager({
settingsManager: this.settingsManager,
onSessionChange: function(sessionData) {
self.handleSessionChange(sessionData);
},
@ -569,6 +570,97 @@
this.uiRenderer.renderStats(stats);
};
/**
* 匯出會話歷史
*/
SessionManager.prototype.exportSessionHistory = function() {
if (!this.dataManager) {
console.error('📋 DataManager 未初始化');
return;
}
try {
const filename = this.dataManager.exportSessionHistory();
// 顯示成功訊息
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
const message = window.i18nManager ?
window.i18nManager.t('sessionHistory.management.exportSuccess') :
'會話歷史已匯出';
window.MCPFeedback.Utils.showMessage(message + ': ' + filename, 'success');
}
} catch (error) {
console.error('📋 匯出會話歷史失敗:', error);
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
window.MCPFeedback.Utils.showMessage('匯出失敗: ' + error.message, 'error');
}
}
};
/**
* 匯出單一會話
*/
SessionManager.prototype.exportSingleSession = function(sessionId) {
if (!this.dataManager) {
console.error('📋 DataManager 未初始化');
return;
}
try {
const filename = this.dataManager.exportSingleSession(sessionId);
if (filename) {
// 顯示成功訊息
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
const message = window.i18nManager ?
window.i18nManager.t('sessionHistory.management.exportSuccess') :
'會話已匯出';
window.MCPFeedback.Utils.showMessage(message + ': ' + filename, 'success');
}
}
} catch (error) {
console.error('📋 匯出單一會話失敗:', error);
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
window.MCPFeedback.Utils.showMessage('匯出失敗: ' + error.message, 'error');
}
}
};
/**
* 清空會話歷史
*/
SessionManager.prototype.clearSessionHistory = function() {
if (!this.dataManager) {
console.error('📋 DataManager 未初始化');
return;
}
// 確認對話框
const confirmMessage = window.i18nManager ?
window.i18nManager.t('sessionHistory.management.confirmClear') :
'確定要清空所有會話歷史嗎?';
if (!confirm(confirmMessage)) {
return;
}
try {
this.dataManager.clearHistory();
// 顯示成功訊息
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
const message = window.i18nManager ?
window.i18nManager.t('sessionHistory.management.clearSuccess') :
'會話歷史已清空';
window.MCPFeedback.Utils.showMessage(message, 'success');
}
} catch (error) {
console.error('📋 清空會話歷史失敗:', error);
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
window.MCPFeedback.Utils.showMessage('清空失敗: ' + error.message, 'error');
}
}
};
/**
* 清理資源
*/
@ -618,6 +710,33 @@
}
};
// 全域匯出會話歷史方法
window.MCPFeedback.SessionManager.exportSessionHistory = function() {
if (window.MCPFeedback && window.MCPFeedback.app && window.MCPFeedback.app.sessionManager) {
window.MCPFeedback.app.sessionManager.exportSessionHistory();
} else {
console.warn('找不到 SessionManager 實例');
}
};
// 全域匯出單一會話方法
window.MCPFeedback.SessionManager.exportSingleSession = function(sessionId) {
if (window.MCPFeedback && window.MCPFeedback.app && window.MCPFeedback.app.sessionManager) {
window.MCPFeedback.app.sessionManager.exportSingleSession(sessionId);
} else {
console.warn('找不到 SessionManager 實例');
}
};
// 全域清空會話歷史方法
window.MCPFeedback.SessionManager.clearSessionHistory = function() {
if (window.MCPFeedback && window.MCPFeedback.app && window.MCPFeedback.app.sessionManager) {
window.MCPFeedback.app.sessionManager.clearSessionHistory();
} else {
console.warn('找不到 SessionManager 實例');
}
};
console.log('✅ SessionManager (重構版) 模組載入完成');
})();

View File

@ -33,12 +33,18 @@
totalSessions: 0
};
// localStorage 相關設定
this.localStorageKey = 'mcp-session-history';
this.settingsManager = options.settingsManager || null;
// 回調函數
this.onSessionChange = options.onSessionChange || null;
this.onHistoryChange = options.onHistoryChange || null;
this.onStatsChange = options.onStatsChange || null;
// 初始化統計資訊
// 初始化:載入歷史記錄並清理過期資料
this.loadFromLocalStorage();
this.cleanupExpiredSessions();
this.updateStats();
console.log('📊 SessionDataManager 初始化完成');
@ -196,6 +202,9 @@
return false;
}
// 新增儲存時間戳記
sessionData.saved_at = TimeUtils.getCurrentTimestamp();
// 避免重複新增
const existingIndex = this.sessionHistory.findIndex(s => s.session_id === sessionData.session_id);
if (existingIndex !== -1) {
@ -209,6 +218,9 @@
this.sessionHistory = this.sessionHistory.slice(0, 10);
}
// 保存到 localStorage
this.saveToLocalStorage();
this.updateStats();
// 觸發回調
@ -295,6 +307,10 @@
*/
SessionDataManager.prototype.clearHistory = function() {
this.sessionHistory = [];
// 清空 localStorage
this.clearLocalStorage();
this.updateStats();
if (this.onHistoryChange) {
this.onHistoryChange(this.sessionHistory);
@ -358,6 +374,191 @@
return '暫無摘要';
};
/**
* localStorage 載入會話歷史
*/
SessionDataManager.prototype.loadFromLocalStorage = function() {
if (!window.localStorage) {
console.warn('📊 localStorage 不可用');
return;
}
try {
const stored = localStorage.getItem(this.localStorageKey);
if (stored) {
const data = JSON.parse(stored);
if (data && Array.isArray(data.sessions)) {
this.sessionHistory = data.sessions;
console.log('📊 從 localStorage 載入', this.sessionHistory.length, '個會話');
}
}
} catch (error) {
console.error('📊 從 localStorage 載入會話歷史失敗:', error);
}
};
/**
* 保存會話歷史到 localStorage
*/
SessionDataManager.prototype.saveToLocalStorage = function() {
if (!window.localStorage) {
console.warn('📊 localStorage 不可用');
return;
}
try {
const data = {
sessions: this.sessionHistory,
lastCleanup: TimeUtils.getCurrentTimestamp()
};
localStorage.setItem(this.localStorageKey, JSON.stringify(data));
console.log('📊 已保存', this.sessionHistory.length, '個會話到 localStorage');
} catch (error) {
console.error('📊 保存會話歷史到 localStorage 失敗:', error);
}
};
/**
* 清空 localStorage 中的會話歷史
*/
SessionDataManager.prototype.clearLocalStorage = function() {
if (!window.localStorage) {
return;
}
try {
localStorage.removeItem(this.localStorageKey);
console.log('📊 已清空 localStorage 中的會話歷史');
} catch (error) {
console.error('📊 清空 localStorage 失敗:', error);
}
};
/**
* 清理過期的會話
*/
SessionDataManager.prototype.cleanupExpiredSessions = function() {
if (!this.settingsManager) {
return;
}
const retentionHours = this.settingsManager.get('sessionHistoryRetentionHours', 72);
const retentionMs = retentionHours * 60 * 60 * 1000;
const now = TimeUtils.getCurrentTimestamp();
const originalCount = this.sessionHistory.length;
this.sessionHistory = this.sessionHistory.filter(function(session) {
const sessionAge = now - (session.saved_at || session.completed_at || session.created_at || 0);
return sessionAge < retentionMs;
});
const cleanedCount = originalCount - this.sessionHistory.length;
if (cleanedCount > 0) {
console.log('📊 清理了', cleanedCount, '個過期會話');
this.saveToLocalStorage();
}
};
/**
* 檢查會話是否過期
*/
SessionDataManager.prototype.isSessionExpired = function(session) {
if (!this.settingsManager) {
return false;
}
const retentionHours = this.settingsManager.get('sessionHistoryRetentionHours', 72);
const retentionMs = retentionHours * 60 * 60 * 1000;
const now = TimeUtils.getCurrentTimestamp();
const sessionTime = session.saved_at || session.completed_at || session.created_at || 0;
return (now - sessionTime) > retentionMs;
};
/**
* 匯出會話歷史
*/
SessionDataManager.prototype.exportSessionHistory = function() {
const exportData = {
exportedAt: new Date().toISOString(),
totalSessions: this.sessionHistory.length,
sessions: this.sessionHistory.map(function(session) {
return {
session_id: session.session_id,
created_at: session.created_at,
completed_at: session.completed_at,
duration: session.duration,
status: session.status,
project_directory: session.project_directory,
ai_summary: session.summary || session.ai_summary,
saved_at: session.saved_at
};
})
};
const filename = 'session-history-' + new Date().toISOString().split('T')[0] + '.json';
this.downloadJSON(exportData, filename);
console.log('📊 匯出了', this.sessionHistory.length, '個會話');
return filename;
};
/**
* 匯出單一會話
*/
SessionDataManager.prototype.exportSingleSession = function(sessionId) {
const session = this.sessionHistory.find(function(s) {
return s.session_id === sessionId;
});
if (!session) {
console.error('📊 找不到會話:', sessionId);
return null;
}
const exportData = {
exportedAt: new Date().toISOString(),
session: {
session_id: session.session_id,
created_at: session.created_at,
completed_at: session.completed_at,
duration: session.duration,
status: session.status,
project_directory: session.project_directory,
ai_summary: session.summary || session.ai_summary,
saved_at: session.saved_at
}
};
const shortId = sessionId.substring(0, 8);
const filename = 'session-' + shortId + '-' + new Date().toISOString().split('T')[0] + '.json';
this.downloadJSON(exportData, filename);
console.log('📊 匯出會話:', sessionId);
return filename;
};
/**
* 下載 JSON 檔案
*/
SessionDataManager.prototype.downloadJSON = function(data, filename) {
try {
const jsonString = JSON.stringify(data, null, 2);
const blob = new Blob([jsonString], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (error) {
console.error('📊 下載檔案失敗:', error);
}
};
/**
* 清理資源
*/

View File

@ -328,19 +328,39 @@
(window.i18nManager ? window.i18nManager.t('sessionManagement.viewDetails') : '查看') :
(window.i18nManager ? window.i18nManager.t('sessionManagement.viewDetails') : '詳細資訊');
const button = DOMUtils.createElement('button', {
const viewButton = DOMUtils.createElement('button', {
className: 'btn-small',
textContent: buttonText
});
// 添加點擊事件
DOMUtils.addEventListener(button, 'click', function() {
// 添加查看詳情點擊事件
DOMUtils.addEventListener(viewButton, 'click', function() {
if (window.MCPFeedback && window.MCPFeedback.SessionManager) {
window.MCPFeedback.SessionManager.viewSessionDetails(sessionData.session_id);
}
});
actions.appendChild(button);
actions.appendChild(viewButton);
// 如果是歷史會話,新增匯出按鈕
if (isHistory) {
const exportButton = DOMUtils.createElement('button', {
className: 'btn-small btn-export',
textContent: window.i18nManager ? window.i18nManager.t('sessionHistory.management.exportSingle') : '匯出',
style: 'margin-left: 4px; font-size: 11px; padding: 2px 6px;'
});
// 添加匯出點擊事件
DOMUtils.addEventListener(exportButton, 'click', function(e) {
e.stopPropagation(); // 防止觸發父元素事件
if (window.MCPFeedback && window.MCPFeedback.SessionManager) {
window.MCPFeedback.SessionManager.exportSingleSession(sessionData.session_id);
}
});
actions.appendChild(exportButton);
}
return actions;
};

View File

@ -35,7 +35,9 @@
audioNotificationEnabled: false,
audioNotificationVolume: 50,
selectedAudioId: 'default-beep',
customAudios: []
customAudios: [],
// 會話歷史設定
sessionHistoryRetentionHours: 72
};
// 當前設定
@ -407,6 +409,9 @@
// 應用自動提交設定
this.applyAutoSubmitSettingsToUI();
// 應用會話歷史設定
this.applySessionHistorySettings();
};
/**
@ -549,7 +554,20 @@
}
};
/**
* 應用會話歷史設定
*/
SettingsManager.prototype.applySessionHistorySettings = function() {
// 更新會話歷史保存期限選擇器
const sessionHistoryRetentionSelect = Utils.safeQuerySelector('#sessionHistoryRetentionHours');
if (sessionHistoryRetentionSelect) {
sessionHistoryRetentionSelect.value = this.currentSettings.sessionHistoryRetentionHours.toString();
}
console.log('會話歷史設定已應用到 UI:', {
retentionHours: this.currentSettings.sessionHistoryRetentionHours
});
};
/**
* 設置事件監聽器
@ -731,6 +749,44 @@
});
}
// 會話歷史保存期限設定
const sessionHistoryRetentionSelect = Utils.safeQuerySelector('#sessionHistoryRetentionHours');
if (sessionHistoryRetentionSelect) {
sessionHistoryRetentionSelect.addEventListener('change', function(e) {
const hours = parseInt(e.target.value);
self.set('sessionHistoryRetentionHours', hours);
console.log('會話歷史保存期限已更新:', hours, '小時');
// 觸發清理過期會話
if (window.MCPFeedback && window.MCPFeedback.app && window.MCPFeedback.app.sessionManager) {
const sessionManager = window.MCPFeedback.app.sessionManager;
if (sessionManager.dataManager && sessionManager.dataManager.cleanupExpiredSessions) {
sessionManager.dataManager.cleanupExpiredSessions();
}
}
});
}
// 會話歷史匯出按鈕
const exportHistoryBtn = Utils.safeQuerySelector('#exportSessionHistoryBtn');
if (exportHistoryBtn) {
exportHistoryBtn.addEventListener('click', function() {
if (window.MCPFeedback && window.MCPFeedback.SessionManager) {
window.MCPFeedback.SessionManager.exportSessionHistory();
}
});
}
// 會話歷史清空按鈕
const clearHistoryBtn = Utils.safeQuerySelector('#clearSessionHistoryBtn');
if (clearHistoryBtn) {
clearHistoryBtn.addEventListener('click', function() {
if (window.MCPFeedback && window.MCPFeedback.SessionManager) {
window.MCPFeedback.SessionManager.clearSessionHistory();
}
});
}
// 重置設定
const resetBtn = Utils.safeQuerySelector('#resetSettingsBtn');
if (resetBtn) {

View File

@ -884,6 +884,48 @@
</div>
</div>
<!-- 會話歷史管理卡片 -->
<div class="settings-card">
<div class="settings-card-header">
<h3 class="settings-card-title" data-i18n="sessionHistory.management.title">📚 會話歷史管理</h3>
</div>
<div class="settings-card-body">
<div class="setting-item">
<div class="setting-info">
<div class="setting-label" data-i18n="sessionHistory.management.retentionPeriod">保存期限</div>
<div class="setting-description" data-i18n="sessionHistory.management.description">
管理本地儲存的會話歷史記錄,包括保存期限設定和資料匯出功能
</div>
</div>
<div class="setting-control">
<select id="sessionHistoryRetentionHours" class="form-select" style="width: 150px;">
<option value="24" data-i18n="sessionHistory.retention.24hours">24 小時</option>
<option value="72" data-i18n="sessionHistory.retention.72hours">72 小時</option>
<option value="168" data-i18n="sessionHistory.retention.168hours">7 天</option>
<option value="720" data-i18n="sessionHistory.retention.720hours">30 天</option>
</select>
</div>
</div>
<div class="setting-item" style="border-bottom: none;">
<div class="setting-info">
<div class="setting-label" data-i18n="sessionHistory.management.export">資料管理</div>
<div class="setting-description">
匯出或清空本地儲存的會話歷史記錄
</div>
</div>
<div class="setting-control" style="display: flex; gap: 8px;">
<button id="exportSessionHistoryBtn" class="btn btn-secondary" style="font-size: 12px; padding: 6px 12px;">
<span data-i18n="sessionHistory.management.exportAll">匯出全部</span>
</button>
<button id="clearSessionHistoryBtn" class="btn btn-secondary" style="font-size: 12px; padding: 6px 12px; color: var(--error-color); border-color: var(--error-color);">
<span data-i18n="sessionHistory.management.clear">清空</span>
</button>
</div>
</div>
</div>
</div>
<!-- 提示詞管理卡片 -->
<div class="settings-card">
<div class="settings-card-header">