🎨 大幅簡化UI

This commit is contained in:
Minidoracat 2025-06-27 22:48:02 +08:00
parent 5f9eb6a42e
commit 7b6b177031
9 changed files with 551 additions and 150 deletions

View File

@ -47,7 +47,10 @@
"processingFeedback": "Processing, please wait", "processingFeedback": "Processing, please wait",
"connectingMessage": "WebSocket connecting, feedback will be submitted automatically when connection is ready...", "connectingMessage": "WebSocket connecting, feedback will be submitted automatically when connection is ready...",
"invalidState": "Current state does not allow submission", "invalidState": "Current state does not allow submission",
"sendFailed": "Send failed, please retry" "sendFailed": "Send failed, please retry",
"noContent": "No content to copy",
"copySuccess": "Content copied to clipboard",
"copyFailed": "Failed to copy"
}, },
"summary": { "summary": {
"title": "📋 AI Work Summary", "title": "📋 AI Work Summary",
@ -321,7 +324,11 @@
"latencyMs": "Latency", "latencyMs": "Latency",
"sessions": "Sessions", "sessions": "Sessions",
"reconnects": "Reconnects" "reconnects": "Reconnects"
} },
"unknown": "Unknown"
},
"stats": {
"detailedStats": "Detailed Statistics"
}, },
"dynamic": { "dynamic": {
@ -380,6 +387,7 @@
}, },
"about": { "about": {
"title": " About", "title": " About",
"description": "A powerful MCP server that provides human-in-the-loop feedback functionality for AI-assisted development tools. Features a Web UI interface with rich capabilities including image upload, command execution, and multi-language support.",
"appInfo": "Application Information", "appInfo": "Application Information",
"version": "Version", "version": "Version",
"projectLinks": "Project Links", "projectLinks": "Project Links",

View File

@ -47,7 +47,10 @@
"processingFeedback": "正在处理中,请稍候", "processingFeedback": "正在处理中,请稍候",
"connectingMessage": "WebSocket 连接中,反馈将在连接就绪后自动提交...", "connectingMessage": "WebSocket 连接中,反馈将在连接就绪后自动提交...",
"invalidState": "当前状态不允许提交", "invalidState": "当前状态不允许提交",
"sendFailed": "发送失败,请重试" "sendFailed": "发送失败,请重试",
"noContent": "没有可复制的内容",
"copySuccess": "内容已复制到剪贴板",
"copyFailed": "复制失败"
}, },
"summary": { "summary": {
"title": "📋 AI 工作摘要", "title": "📋 AI 工作摘要",
@ -380,6 +383,7 @@
}, },
"about": { "about": {
"title": " 关于", "title": " 关于",
"description": "一个强大的 MCP 服务器,为 AI 辅助开发工具提供人在回路的互动反馈功能。支持 Web UI 界面,并具备图片上传、命令执行、多语言等丰富功能。",
"appInfo": "应用程序信息", "appInfo": "应用程序信息",
"version": "版本", "version": "版本",
"projectLinks": "项目链接", "projectLinks": "项目链接",
@ -463,5 +467,8 @@
"testPlaying": "正在播放测试音效", "testPlaying": "正在播放测试音效",
"audioNotFound": "找不到选择的音效" "audioNotFound": "找不到选择的音效"
} }
},
"stats": {
"detailedStats": "详细统计信息"
} }
} }

View File

@ -52,7 +52,10 @@
"processingFeedback": "正在處理中,請稍候", "processingFeedback": "正在處理中,請稍候",
"connectingMessage": "WebSocket 連接中,回饋將在連接就緒後自動提交...", "connectingMessage": "WebSocket 連接中,回饋將在連接就緒後自動提交...",
"invalidState": "當前狀態不允許提交", "invalidState": "當前狀態不允許提交",
"sendFailed": "發送失敗,請重試" "sendFailed": "發送失敗,請重試",
"noContent": "沒有可複製的內容",
"copySuccess": "內容已複製到剪貼板",
"copyFailed": "複製失敗"
}, },
"summary": { "summary": {
"title": "📋 AI 工作摘要", "title": "📋 AI 工作摘要",
@ -385,6 +388,7 @@
}, },
"about": { "about": {
"title": " 關於", "title": " 關於",
"description": "一個強大的 MCP 伺服器,為 AI 輔助開發工具提供人在回路的互動回饋功能。支援 Web UI 介面,並具備圖片上傳、命令執行、多語言等豐富功能。",
"appInfo": "應用程式資訊", "appInfo": "應用程式資訊",
"version": "版本", "version": "版本",
"projectLinks": "專案連結", "projectLinks": "專案連結",
@ -468,5 +472,8 @@
"testPlaying": "正在播放測試音效", "testPlaying": "正在播放測試音效",
"audioNotFound": "找不到選擇的音效" "audioNotFound": "找不到選擇的音效"
} }
},
"stats": {
"detailedStats": "詳細統計資訊"
} }
} }

View File

@ -51,6 +51,86 @@
gap: 20px; gap: 20px;
} }
/* 緊湊版頂部欄 */
.connection-monitor-bar.compact {
padding: 4px 12px;
gap: 0;
font-size: 12px;
height: 32px;
align-items: center;
justify-content: flex-start;
}
/* 緊湊版元素樣式 */
.app-title-compact {
font-weight: 600;
color: var(--text-primary);
font-size: 13px;
white-space: nowrap;
}
.info-separator {
margin: 0 6px;
color: var(--text-secondary);
opacity: 0.4;
font-size: 10px;
}
.project-info-compact,
.session-info-compact,
.countdown-display-compact,
.connection-status-compact {
display: flex;
align-items: center;
gap: 4px;
white-space: nowrap;
font-size: 11px;
}
.project-info-compact .project-path-display {
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
font-size: 11px;
}
.session-info-compact .session-id-display {
font-size: 11px;
padding: 1px 4px;
}
.countdown-display-compact {
color: var(--warning-color);
}
.countdown-display-compact .countdown-timer {
font-weight: bold;
font-size: 12px;
}
.connection-status-compact {
margin-left: auto;
}
.connection-status-compact .status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
.connection-status-compact.connected {
color: var(--status-connected);
}
.connection-status-compact.disconnected {
color: var(--status-disconnected);
}
.connection-status-compact.connecting {
color: var(--status-connecting);
}
/* 應用資訊區域 */ /* 應用資訊區域 */
.app-info-section { .app-info-section {
display: flex; display: flex;
@ -648,42 +728,37 @@
/* ===== 響應式設計 ===== */ /* ===== 響應式設計 ===== */
@media (max-width: 768px) { @media (max-width: 768px) {
.connection-monitor-bar { /* 緊湊版頂部欄在移動設備上的調整 */
flex-direction: column; .connection-monitor-bar.compact {
gap: 8px; padding: 4px 10px;
padding: 12px 16px; font-size: 11px;
overflow-x: auto;
white-space: nowrap;
} }
.app-info-section { /* 隱藏專案路徑以節省空間 */
width: 100%; .project-info-compact {
text-align: center; display: none;
} }
.app-title { /* 隱藏相關的分隔符 */
justify-content: center; .project-info-compact + .info-separator {
flex-wrap: wrap; display: none;
} }
.app-title h1 { /* 調整標題字體 */
font-size: 16px; .app-title-compact {
font-size: 12px;
} }
.connection-status-group { /* 減少分隔符間距 */
width: 100%; .info-separator {
justify-content: center; margin: 0 6px;
flex-wrap: wrap;
} }
.connection-status-combined { /* 確保倒數計時器始終可見 */
width: 100%; .countdown-display-compact {
align-items: center; font-weight: bold;
}
.detailed-status-info {
margin-left: 0;
margin-top: 0;
justify-content: center;
flex-wrap: wrap;
} }
/* 會話管理頁籤響應式調整 */ /* 會話管理頁籤響應式調整 */
@ -697,6 +772,28 @@
} }
} }
/* 超小螢幕調整 */
@media (max-width: 480px) {
/* 隱藏會話 ID */
.session-info-compact {
display: none;
}
/* 隱藏相關的分隔符 */
.session-info-compact + .info-separator {
display: none;
}
/* 保留核心資訊:標題、倒數計時器、連線狀態 */
.connection-monitor-bar.compact {
justify-content: space-between;
}
.connection-status-compact {
margin-left: 0;
}
}
/* ===== 載入狀態 ===== */ /* ===== 載入狀態 ===== */
.loading-skeleton { .loading-skeleton {
background: linear-gradient(90deg, background: linear-gradient(90deg,
@ -1204,3 +1301,32 @@
transition-duration: 0.01ms !important; transition-duration: 0.01ms !important;
} }
} }
/* 響應式設計 - 緊湊版頂部欄 */
@media (max-width: 768px) {
/* 小螢幕隱藏專案路徑 */
.connection-monitor-bar.compact .project-info-compact {
display: none;
}
/* 隱藏相鄰的分隔符 */
.connection-monitor-bar.compact .project-info-compact + .info-separator {
display: none;
}
}
@media (max-width: 480px) {
/* 極小螢幕只顯示最關鍵資訊 */
.connection-monitor-bar.compact .session-info-compact {
display: none;
}
.connection-monitor-bar.compact .session-info-compact + .info-separator {
display: none;
}
/* 調整標題字體大小 */
.app-title-compact {
font-size: 12px;
}
}

View File

@ -98,6 +98,114 @@
z-index: 1000; z-index: 1000;
} }
/* ===== 可折疊統計面板 ===== */
.stats-panel-floating {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: var(--bg-secondary);
border-top: 1px solid var(--border-color);
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
z-index: 100;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform;
}
/* 收起狀態 */
.stats-panel-floating.collapsed {
transform: translateY(calc(100% - 40px));
}
/* 統計面板頭部 */
.stats-panel-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
cursor: pointer;
user-select: none;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border-color);
}
.stats-panel-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
font-weight: 500;
color: var(--text-primary);
}
.stats-toggle-icon {
transition: transform 0.3s ease;
}
.stats-panel-floating.collapsed .stats-toggle-icon {
transform: rotate(180deg);
}
/* 統計面板內容 */
.stats-panel-content {
padding: 16px 20px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
max-width: 1200px;
margin: 0 auto;
}
/* 統計項目 */
.stats-item-detailed {
display: flex;
flex-direction: column;
gap: 4px;
padding: 12px;
background: var(--bg-primary);
border-radius: 6px;
border: 1px solid var(--border-color);
}
.stats-item-label {
font-size: 11px;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.stats-item-value {
font-size: 16px;
font-weight: 600;
color: var(--accent-color);
font-family: 'Consolas', 'Monaco', monospace;
}
/* 動畫效能優化 */
@media (prefers-reduced-motion: reduce) {
.stats-panel-floating {
transition: none;
}
.stats-toggle-icon {
transition: none;
}
}
/* 響應式設計 */
@media (max-width: 768px) {
.stats-panel-content {
grid-template-columns: 1fr;
gap: 12px;
padding: 12px 16px;
}
.stats-item-detailed {
padding: 10px;
}
}
.tooltip:hover::after { .tooltip:hover::after {
opacity: 1; opacity: 1;
} }
@ -371,7 +479,7 @@ body {
.container { .container {
width: 100%; width: 100%;
margin: 0 auto; margin: 0 auto;
padding: 20px; padding: 12px;
background: var(--bg-primary); background: var(--bg-primary);
color: var(--text-primary); color: var(--text-primary);
min-height: 100vh; min-height: 100vh;
@ -950,7 +1058,7 @@ body {
flex-direction: column; flex-direction: column;
min-width: 0; min-width: 0;
min-height: 0; /* 允許內容自然收縮 */ min-height: 0; /* 允許內容自然收縮 */
padding: 20px; padding: 12px;
overflow: hidden; overflow: hidden;
transition: all 0.6s cubic-bezier(0.4, 0.0, 0.2, 1); transition: all 0.6s cubic-bezier(0.4, 0.0, 0.2, 1);
background: var(--bg-primary); background: var(--bg-primary);
@ -968,7 +1076,7 @@ body {
/* 分頁樣式 */ /* 分頁樣式 */
.tabs { .tabs {
border-bottom: 2px solid var(--border-color); border-bottom: 2px solid var(--border-color);
margin-bottom: 20px; margin-bottom: 12px;
} }
.tab-buttons { .tab-buttons {
@ -980,7 +1088,7 @@ body {
background: transparent; background: transparent;
border: none; border: none;
color: var(--text-secondary); color: var(--text-secondary);
padding: 12px 20px; padding: 10px 16px;
cursor: pointer; cursor: pointer;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
transition: all 0.3s ease; transition: all 0.3s ease;

View File

@ -314,6 +314,15 @@
}); });
} }
// 複製用戶內容按鈕
const copyUserFeedback = window.MCPFeedback.Utils.safeQuerySelector('#copyUserFeedback');
if (copyUserFeedback) {
copyUserFeedback.addEventListener('click', function(e) {
e.preventDefault();
self.copyUserFeedback();
});
}
// 快捷鍵 // 快捷鍵
document.addEventListener('keydown', function(e) { document.addEventListener('keydown', function(e) {
// Ctrl+Enter 提交回饋 // Ctrl+Enter 提交回饋
@ -1058,8 +1067,14 @@
}); });
if (success) { if (success) {
// 清空表單 // 重置表單狀態但保留文字內容
this.clearFeedback(); if (this.uiManager) {
this.uiManager.resetFeedbackForm(false); // false 表示不清空文字
}
// 只清空圖片
if (this.imageHandler) {
this.imageHandler.clearImages();
}
console.log('📤 回饋已發送,等待服務器確認...'); console.log('📤 回饋已發送,等待服務器確認...');
} else { } else {
throw new Error('WebSocket 發送失敗'); throw new Error('WebSocket 發送失敗');
@ -1121,9 +1136,9 @@
FeedbackApp.prototype.clearFeedback = function() { FeedbackApp.prototype.clearFeedback = function() {
console.log('🧹 清空回饋內容...'); console.log('🧹 清空回饋內容...');
// 使用 UI 管理器重置表單 // 使用 UI 管理器重置表單,並清空文字
if (this.uiManager) { if (this.uiManager) {
this.uiManager.resetFeedbackForm(); this.uiManager.resetFeedbackForm(true); // 傳入 true 表示要清空文字
} }
// 清空圖片數據 // 清空圖片數據
@ -1134,6 +1149,57 @@
console.log('✅ 回饋內容清空完成'); console.log('✅ 回饋內容清空完成');
}; };
/**
* 複製用戶回饋內容
*/
FeedbackApp.prototype.copyUserFeedback = function() {
console.log('📋 複製用戶回饋內容...');
const feedbackInput = window.MCPFeedback.Utils.safeQuerySelector('#combinedFeedbackText');
if (!feedbackInput || !feedbackInput.value.trim()) {
window.MCPFeedback.Utils.showMessage(
window.i18nManager ? window.i18nManager.t('feedback.noContent') : '沒有可複製的內容',
window.MCPFeedback.Utils.CONSTANTS.MESSAGE_WARNING
);
return;
}
const textContent = feedbackInput.value;
// 複製到剪貼板
navigator.clipboard.writeText(textContent)
.then(function() {
console.log('✅ 內容已複製到剪貼板');
window.MCPFeedback.Utils.showMessage(
window.i18nManager ? window.i18nManager.t('feedback.copySuccess') : '內容已複製到剪貼板',
window.MCPFeedback.Utils.CONSTANTS.MESSAGE_SUCCESS
);
})
.catch(function(err) {
console.error('❌ 複製失敗:', err);
// 降級方案:使用舊的複製方法
const textarea = document.createElement('textarea');
textarea.value = textContent;
textarea.style.position = 'fixed';
textarea.style.left = '-999999px';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
window.MCPFeedback.Utils.showMessage(
window.i18nManager ? window.i18nManager.t('feedback.copySuccess') : '內容已複製到剪貼板',
window.MCPFeedback.Utils.CONSTANTS.MESSAGE_SUCCESS
);
} catch (error) {
window.MCPFeedback.Utils.showMessage(
window.i18nManager ? window.i18nManager.t('feedback.copyFailed') : '複製失敗',
window.MCPFeedback.Utils.CONSTANTS.MESSAGE_ERROR
);
}
document.body.removeChild(textarea);
});
};
/** /**
* 取消回饋 * 取消回饋
*/ */
@ -1298,7 +1364,7 @@
if (self.uiManager) { if (self.uiManager) {
// console.log('🔧 準備更新 AI 摘要內容summary 長度:', sessionData.summary ? sessionData.summary.length : 'undefined'); // console.log('🔧 準備更新 AI 摘要內容summary 長度:', sessionData.summary ? sessionData.summary.length : 'undefined');
self.uiManager.updateAISummaryContent(sessionData.summary); self.uiManager.updateAISummaryContent(sessionData.summary);
self.uiManager.resetFeedbackForm(); self.uiManager.resetFeedbackForm(false); // 不清空文字內容
self.uiManager.updateStatusIndicator(); self.uiManager.updateStatusIndicator();
} }

View File

@ -133,6 +133,36 @@
indicator.className = 'connection-indicator ' + status; indicator.className = 'connection-indicator ' + status;
} }
// 更新精簡的頂部狀態指示器(現在是緊湊版)
const minimalIndicator = document.getElementById('connectionStatusMinimal');
if (minimalIndicator) {
minimalIndicator.className = 'connection-status-compact ' + status;
const statusText = minimalIndicator.querySelector('.status-text');
if (statusText) {
let statusKey = '';
switch (status) {
case 'connected':
statusKey = 'connectionMonitor.connected';
break;
case 'connecting':
statusKey = 'connectionMonitor.connecting';
break;
case 'disconnected':
statusKey = 'connectionMonitor.disconnected';
break;
case 'reconnecting':
statusKey = 'connectionMonitor.reconnecting';
break;
default:
statusKey = 'connectionMonitor.unknown';
}
statusText.setAttribute('data-i18n', statusKey);
if (window.i18nManager) {
statusText.textContent = window.i18nManager.t(statusKey);
}
}
}
// 處理特殊狀態 // 處理特殊狀態
switch (status) { switch (status) {
case 'connected': case 'connected':
@ -282,15 +312,30 @@
} }
} }
// 更新統計面板中的延遲顯示
const statsLatency = document.getElementById('statsLatency');
if (statsLatency) {
statsLatency.textContent = this.currentLatency > 0 ? this.currentLatency + 'ms' : '--ms';
}
// 更新連線時間 // 更新連線時間
if (this.connectionTimeDisplay && this.connectionStartTime) { let connectionTimeStr = '--:--';
if (this.connectionStartTime) {
const duration = Math.floor((Date.now() - this.connectionStartTime) / 1000); const duration = Math.floor((Date.now() - this.connectionStartTime) / 1000);
const minutes = Math.floor(duration / 60); const minutes = Math.floor(duration / 60);
const seconds = duration % 60; const seconds = duration % 60;
connectionTimeStr = String(minutes).padStart(2, '0') + ':' + String(seconds).padStart(2, '0');
}
if (this.connectionTimeDisplay) {
const connectionTimeLabel = window.i18nManager ? window.i18nManager.t('connectionMonitor.connectionTime') : '連線時間'; const connectionTimeLabel = window.i18nManager ? window.i18nManager.t('connectionMonitor.connectionTime') : '連線時間';
this.connectionTimeDisplay.textContent = connectionTimeLabel + ': ' + this.connectionTimeDisplay.textContent = connectionTimeLabel + ': ' + connectionTimeStr;
String(minutes).padStart(2, '0') + ':' + }
String(seconds).padStart(2, '0');
// 更新統計面板中的連線時間
const statsConnectionTime = document.getElementById('statsConnectionTime');
if (statsConnectionTime) {
statsConnectionTime.textContent = connectionTimeStr;
} }
// 更新重連次數 // 更新重連次數
@ -300,10 +345,35 @@
this.reconnectCountDisplay.textContent = reconnectLabel + ': ' + this.reconnectCount + ' ' + timesLabel; this.reconnectCountDisplay.textContent = reconnectLabel + ': ' + this.reconnectCount + ' ' + timesLabel;
} }
// 更新統計面板中的重連次數
const statsReconnectCount = document.getElementById('statsReconnectCount');
if (statsReconnectCount) {
statsReconnectCount.textContent = this.reconnectCount.toString();
}
// 更新訊息計數 // 更新訊息計數
if (this.messageCountDisplay) { if (this.messageCountDisplay) {
this.messageCountDisplay.textContent = this.messageCount; this.messageCountDisplay.textContent = this.messageCount;
} }
// 更新統計面板中的訊息計數
const statsMessageCount = document.getElementById('statsMessageCount');
if (statsMessageCount) {
statsMessageCount.textContent = this.messageCount.toString();
}
// 更新統計面板中的會話數和狀態
const sessionCount = document.getElementById('sessionCount');
const statsSessionCount = document.getElementById('statsSessionCount');
if (sessionCount && statsSessionCount) {
statsSessionCount.textContent = sessionCount.textContent;
}
const sessionStatusText = document.getElementById('sessionStatusText');
const statsSessionStatus = document.getElementById('statsSessionStatus');
if (sessionStatusText && statsSessionStatus) {
statsSessionStatus.textContent = sessionStatusText.textContent;
}
}; };
/** /**

View File

@ -482,14 +482,18 @@
/** /**
* 重置回饋表單 * 重置回饋表單
* @param {boolean} clearText - 是否清空文字內容預設為 false
*/ */
UIManager.prototype.resetFeedbackForm = function() { UIManager.prototype.resetFeedbackForm = function(clearText) {
console.log('🔄 重置回饋表單...'); console.log('🔄 重置回饋表單...');
// 清空回饋輸入 // 根據參數決定是否清空回饋輸入
const feedbackInput = Utils.safeQuerySelector('#combinedFeedbackText'); const feedbackInput = Utils.safeQuerySelector('#combinedFeedbackText');
if (feedbackInput) { if (feedbackInput) {
feedbackInput.value = ''; if (clearText === true) {
feedbackInput.value = '';
console.log('📝 已清空文字內容');
}
feedbackInput.disabled = false; feedbackInput.disabled = false;
} }

View File

@ -396,85 +396,51 @@
</style> </style>
</head> </head>
<body class="layout-{{ layout_mode }}"> <body class="layout-{{ layout_mode }}">
<!-- ===== 頂部連線監控狀態列 ===== --> <!-- ===== 頂部連線監控狀態列(緊湊版) ===== -->
<div class="connection-monitor-bar"> <div class="connection-monitor-bar compact">
<!-- 左側:應用標題和專案資訊 --> <!-- 標題 -->
<div class="app-info-section"> <div class="app-title-compact">
<div class="app-title"> <span data-i18n="app.title">MCP Feedback</span>
<h1 data-i18n="app.title">MCP Feedback Enhanced</h1>
</div>
<div class="project-info">
📂 <span data-i18n="app.projectDirectory">專案目錄</span>:
<span id="projectPathDisplay" class="project-path-display"
data-full-path="{{ project_directory }}"
data-i18n-title="app.clickToCopyPath"
title="點擊複製完整路徑">{{ project_directory }}</span>
</div>
</div> </div>
<!-- 中間:連線狀態資訊 --> <!-- 分隔符 -->
<div class="connection-status-group"> <span class="info-separator">·</span>
<!-- 左側:會話狀態資訊 -->
<div class="session-status-info">
<div class="current-session-info">
<span class="session-indicator">
📋 <span data-i18n="sessionManagement.currentSession">當前會話</span>:
<span id="currentSessionId" class="session-id-display"
data-full-id="{{ session_id if session_id else 'loading' }}"
data-i18n-title="app.clickToCopySessionId"
title="點擊複製完整會話ID">{{ session_id[:8] if session_id else 'loading' }}...</span>
</span>
<span class="session-age">
<span data-i18n="sessionManagement.activeTime">活躍時間</span>: <span id="sessionAge">--</span>
</span>
</div>
</div>
<!-- 倒數計時器顯示 --> <!-- 專案路徑 -->
<div id="countdownDisplay" class="countdown-display" style="display: none;"> <div class="project-info-compact">
<span class="countdown-label" data-i18n="autoSubmit.countdownLabel">提交倒數</span> <span>📂</span>
<span id="countdownTimer" class="countdown-timer">--:--</span> <span id="projectPathDisplay" class="project-path-display"
</div> data-full-path="{{ project_directory }}"
data-i18n-title="app.clickToCopyPath"
<!-- 主要連線狀態 --> title="點擊複製完整路徑">{{ project_directory[-30:] if project_directory|length > 30 else project_directory }}</span>
<div class="connection-indicator connecting" id="mainConnectionStatus">
<div class="status-icon pulse"></div>
<span class="status-text" data-i18n="connectionMonitor.connecting">連接中...</span>
<div class="connection-quality">
<div class="latency-indicator"><span data-i18n="connectionMonitor.latency">延遲</span>: --ms</div>
<div class="signal-strength">
<div class="signal-bar"></div>
<div class="signal-bar"></div>
<div class="signal-bar"></div>
</div>
</div>
</div>
<!-- 連線和狀態資訊組合 -->
<div class="connection-status-combined">
<!-- 連線詳細資訊 -->
<div class="connection-details">
<span class="connection-time"><span data-i18n="connectionMonitor.connectionTime">連線時間</span>: --:--</span>
<span class="reconnect-count"><span data-i18n="connectionMonitor.reconnectCount">重連</span>: 0 <span data-i18n="connectionMonitor.times"></span></span>
</div>
<!-- 詳細狀態資訊 -->
<div class="detailed-status-info">
<div class="websocket-metrics">
<span class="metric"><span data-i18n="connectionMonitor.metrics.messages">訊息</span>: <span id="messageCount">0</span></span>
<span class="metric"><span data-i18n="connectionMonitor.metrics.latencyMs">延遲</span>: <span id="latencyDisplay">--ms</span></span>
</div>
<div class="session-metrics">
<span class="metric"><span data-i18n="connectionMonitor.metrics.sessions">會話</span>: <span id="sessionCount">1</span></span>
<span class="metric"><span data-i18n="connectionMonitor.statusText">狀態</span>: <span id="sessionStatusText" data-i18n="connectionMonitor.waiting">等待中</span></span>
</div>
</div>
</div>
</div> </div>
<!-- 右側:快速操作 --> <!-- 分隔符 -->
<div class="quick-actions"> <span class="info-separator">·</span>
<!-- 保留空間以保持佈局平衡 -->
<!-- 會話 ID -->
<div class="session-info-compact">
<span>📋</span>
<span id="currentSessionId" class="session-id-display"
data-full-id="{{ session_id if session_id else 'loading' }}"
data-i18n-title="app.clickToCopySessionId"
title="點擊複製完整會話ID">{{ session_id[:6] if session_id else '--' }}</span>
</div>
<!-- 倒數計時器(條件顯示) -->
<div id="countdownDisplay" class="countdown-display-compact" style="display: none;">
<span class="info-separator">·</span>
<span>⏱️</span>
<span id="countdownTimer" class="countdown-timer">--:--</span>
</div>
<!-- 分隔符 -->
<span class="info-separator">·</span>
<!-- 連線狀態 -->
<div class="connection-status-compact" id="connectionStatusMinimal">
<span class="status-dot"></span>
<span class="status-text" data-i18n="connectionMonitor.connected">已連線</span>
</div> </div>
</div> </div>
@ -517,10 +483,6 @@
<!-- ===== AI 摘要分頁 ===== --> <!-- ===== AI 摘要分頁 ===== -->
<div id="tab-summary" class="tab-content"> <div id="tab-summary" class="tab-content">
<div class="section-description" data-i18n="summary.description">
以下是 AI 助手完成的工作摘要,請仔細查看並提供您的回饋意見。
</div>
<div class="input-group"> <div class="input-group">
<div id="summaryContent" class="text-input" style="min-height: 300px; cursor: text; padding: 12px; line-height: 1.6; word-wrap: break-word; overflow-wrap: break-word;" data-dynamic-content="aiSummary"> <div id="summaryContent" class="text-input" style="min-height: 300px; cursor: text; padding: 12px; line-height: 1.6; word-wrap: break-word; overflow-wrap: break-word;" data-dynamic-content="aiSummary">
{{ summary }} {{ summary }}
@ -579,29 +541,9 @@
</button> </button>
</div> </div>
<!-- 等待回饋狀態指示器 -->
{% set id = "combinedFeedbackStatusIndicator" %}
{% set status = "waiting" %}
{% set icon = "⏳" %}
{% set title = "等待回饋" %}
{% set message = "請提供您的回饋意見" %}
{% include 'components/status-indicator.html' %}
<div class="input-group"> <div class="input-group">
<label class="input-label" data-i18n="feedback.textLabel">文字回饋</label> <label class="input-label" data-i18n="feedback.textLabel">文字回饋</label>
<!-- 提示詞按鈕 -->
<div class="prompt-input-buttons" id="combinedPromptButtons">
<button type="button" class="prompt-input-btn select-prompt-btn" data-container-index="1">
<span>📝</span>
<span class="button-text" data-i18n="prompts.buttons.selectPrompt">Templates</span>
</button>
<button type="button" class="prompt-input-btn last-prompt-btn" data-container-index="1">
<span>🔄</span>
<span class="button-text" data-i18n="prompts.buttons.useLastPrompt">Last Used</span>
</button>
</div>
<textarea <textarea
id="combinedFeedbackText" id="combinedFeedbackText"
class="text-input" class="text-input"
@ -613,6 +555,22 @@
• 按 Ctrl+V/Cmd+V 可直接貼上剪貼板圖片" • 按 Ctrl+V/Cmd+V 可直接貼上剪貼板圖片"
style="min-height: 150px;" style="min-height: 150px;"
></textarea> ></textarea>
<!-- 提示詞按鈕 - 移至輸入框下方 -->
<div class="prompt-input-buttons" id="combinedPromptButtons" style="margin-top: 8px;">
<button type="button" class="prompt-input-btn select-prompt-btn" data-container-index="1">
<span>📝</span>
<span class="button-text" data-i18n="prompts.buttons.selectPrompt">Templates</span>
</button>
<button type="button" class="prompt-input-btn last-prompt-btn" data-container-index="1">
<span>🔄</span>
<span class="button-text" data-i18n="prompts.buttons.useLastPrompt">Last Used</span>
</button>
<button type="button" class="prompt-input-btn copy-user-content-btn" id="copyUserFeedback">
<span>📋</span>
<span class="button-text" data-i18n="sessionManagement.copyUserContent">複製用戶內容</span>
</button>
</div>
</div> </div>
<!-- 圖片上傳組件 --> <!-- 圖片上傳組件 -->
@ -998,7 +956,6 @@
<div class="setting-item" style="border-bottom: none; padding-bottom: 16px;"> <div class="setting-item" style="border-bottom: none; padding-bottom: 16px;">
<div class="setting-info"> <div class="setting-info">
<div class="setting-description" data-i18n="about.description" style="color: var(--text-secondary); font-size: 13px; line-height: 1.5;"> <div class="setting-description" data-i18n="about.description" style="color: var(--text-secondary); font-size: 13px; line-height: 1.5;">
一個強大的 MCP 伺服器,為 AI 輔助開發工具提供人在回路的互動回饋功能。支援 Web UI 介面,並具備圖片上傳、命令執行、多語言等豐富功能。
</div> </div>
</div> </div>
</div> </div>
@ -1203,5 +1160,53 @@
initializeApp(); initializeApp();
} }
</script> </script>
<!-- 可折疊統計面板 -->
<div class="stats-panel-floating collapsed" id="statsPanel">
<div class="stats-panel-header" onclick="toggleStatsPanel()">
<div class="stats-panel-title">
<span>📊</span>
<span data-i18n="stats.detailedStats">詳細統計資訊</span>
</div>
<span class="stats-toggle-icon"></span>
</div>
<div class="stats-panel-content">
<!-- 連線資訊 -->
<div class="stats-item-detailed">
<span class="stats-item-label" data-i18n="connectionMonitor.connectionTime">連線時間</span>
<span class="stats-item-value" id="statsConnectionTime">--:--</span>
</div>
<div class="stats-item-detailed">
<span class="stats-item-label" data-i18n="connectionMonitor.reconnectCount">重連次數</span>
<span class="stats-item-value" id="statsReconnectCount">0</span>
</div>
<!-- WebSocket 統計 -->
<div class="stats-item-detailed">
<span class="stats-item-label" data-i18n="connectionMonitor.metrics.messages">訊息數</span>
<span class="stats-item-value" id="statsMessageCount">0</span>
</div>
<div class="stats-item-detailed">
<span class="stats-item-label" data-i18n="connectionMonitor.metrics.latencyMs">延遲</span>
<span class="stats-item-value" id="statsLatency">--ms</span>
</div>
<!-- 會話統計 -->
<div class="stats-item-detailed">
<span class="stats-item-label" data-i18n="connectionMonitor.metrics.sessions">會話數</span>
<span class="stats-item-value" id="statsSessionCount">1</span>
</div>
<div class="stats-item-detailed">
<span class="stats-item-label" data-i18n="connectionMonitor.statusText">狀態</span>
<span class="stats-item-value" id="statsSessionStatus" data-i18n="connectionMonitor.waiting">等待中</span>
</div>
</div>
</div>
<script>
// 統計面板切換功能
function toggleStatsPanel() {
const panel = document.getElementById('statsPanel');
panel.classList.toggle('collapsed');
}
</script>
</body> </body>
</html> </html>