🎨 上方版面調整

This commit is contained in:
Minidoracat 2025-06-14 11:38:14 +08:00
parent a48b2ac795
commit d6284338c6
9 changed files with 801 additions and 33 deletions

View File

@ -3,6 +3,12 @@
"title": "MCP Interactive Feedback System",
"subtitle": "AI Assistant Interactive Feedback Platform",
"projectDirectory": "Project Directory",
"clickToCopyPath": "Click to copy full path",
"clickToCopySessionId": "Click to copy full session ID",
"pathCopied": "Project path copied to clipboard",
"pathCopyFailed": "Failed to copy path",
"sessionIdCopied": "Session ID copied to clipboard",
"sessionIdCopyFailed": "Failed to copy session ID",
"updateFailed": "Failed to update content, please manually refresh the page to view new AI work summary"
},
"tabs": {

View File

@ -3,6 +3,12 @@
"title": "MCP 交互反馈系统",
"subtitle": "AI 助手交互反馈平台",
"projectDirectory": "项目目录",
"clickToCopyPath": "点击复制完整路径",
"clickToCopySessionId": "点击复制完整会话ID",
"pathCopied": "项目路径已复制到剪贴板",
"pathCopyFailed": "复制路径失败",
"sessionIdCopied": "会话ID已复制到剪贴板",
"sessionIdCopyFailed": "复制会话ID失败",
"updateFailed": "更新内容失败,请手动刷新页面以查看新的 AI 工作摘要"
},
"tabs": {

View File

@ -8,6 +8,12 @@
"authorLink": "GitHub: Minidoracat",
"credits": "⭐ 如果這個專案對您有幫助,請在 GitHub 上給我們一個星星!\n\n本增強版本由 Minidoracat 開發和維護,大幅擴展了專案功能,新增了 Web UI 介面、圖片支援、多語言能力以及許多其他改進功能。\n\n同時感謝 sanshao85 的 mcp-feedback-collector 專案提供的 UI 設計靈感。\n\n開源協作讓技術變得更美好",
"projectDirectory": "專案目錄",
"clickToCopyPath": "點擊複製完整路徑",
"clickToCopySessionId": "點擊複製完整會話ID",
"pathCopied": "專案路徑已複製到剪貼板",
"pathCopyFailed": "複製路徑失敗",
"sessionIdCopied": "會話ID已複製到剪貼板",
"sessionIdCopyFailed": "複製會話ID失敗",
"updateFailed": "更新內容失敗,請手動刷新頁面以查看新的 AI 工作摘要"
},
"tabs": {

View File

@ -107,6 +107,170 @@
align-items: center;
gap: 16px;
flex: 2;
justify-content: space-between;
}
/* 會話狀態資訊區域 */
.session-status-info {
display: flex;
flex-direction: column;
gap: 4px;
flex-shrink: 0;
min-width: 0;
}
.session-status-info .current-session-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.session-status-info .session-indicator {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--text-secondary);
}
.session-status-info .session-age {
font-size: 10px;
color: var(--text-secondary);
opacity: 0.8;
margin-left: 20px;
}
/* 會話ID顯示樣式 */
.session-id-display {
font-family: 'Consolas', 'Monaco', monospace;
color: var(--accent-color);
cursor: pointer;
padding: 2px 6px;
border-radius: 4px;
background: rgba(0, 122, 204, 0.1);
border: 1px solid transparent;
transition: all var(--transition-fast) ease;
position: relative;
font-weight: 500;
}
.session-id-display:hover {
background: rgba(0, 122, 204, 0.2);
border-color: var(--accent-color);
transform: scale(1.02);
}
.session-id-display:active {
transform: scale(0.98);
}
/* 會話ID tooltip */
.session-id-display::after {
content: attr(data-full-id);
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
background: var(--bg-primary);
color: var(--text-primary);
padding: 6px 10px;
border-radius: 6px;
font-size: 11px;
white-space: nowrap;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
border: 1px solid var(--border-color);
opacity: 0;
visibility: hidden;
transition: all var(--transition-fast) ease;
z-index: 1000;
margin-top: 4px;
}
.session-id-display:hover::after {
opacity: 1;
visibility: visible;
}
/* 專案路徑顯示樣式 - 模仿會話ID顯示 */
.project-path-display {
font-family: 'Consolas', 'Monaco', monospace;
color: var(--accent-color);
cursor: pointer;
padding: 2px 6px;
border-radius: 4px;
background: rgba(0, 122, 204, 0.1);
border: 1px solid transparent;
transition: all var(--transition-fast) ease;
position: relative;
font-weight: 500;
font-size: 11px;
}
.project-path-display:hover {
background: rgba(0, 122, 204, 0.2);
border-color: var(--accent-color);
transform: scale(1.02);
}
.project-path-display:active {
transform: scale(0.98);
}
/* 專案路徑 tooltip */
.project-path-display::after {
content: attr(data-full-path);
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
background: var(--bg-primary);
color: var(--text-primary);
padding: 6px 10px;
border-radius: 6px;
font-size: 11px;
white-space: nowrap;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
border: 1px solid var(--border-color);
opacity: 0;
visibility: hidden;
transition: all var(--transition-fast) ease;
z-index: 1000;
margin-top: 4px;
max-width: 400px;
word-break: break-all;
}
.project-path-display:hover::after {
opacity: 1;
visibility: visible;
}
/* 專案路徑 tooltip 位置自動調整 */
.project-path-display.tooltip-up::after {
top: auto;
bottom: 100%;
margin-top: 0;
margin-bottom: 4px;
}
.project-path-display.tooltip-left::after {
left: 0;
transform: translateX(0);
}
.project-path-display.tooltip-right::after {
left: auto;
right: 0;
transform: translateX(0);
}
/* 連線和狀態資訊組合容器 */
.connection-status-combined {
display: flex;
flex-direction: column;
gap: 8px;
align-items: center;
flex: 1;
justify-content: center;
}
@ -114,7 +278,7 @@
.detailed-status-info {
display: flex;
gap: 16px;
margin-left: 16px;
margin-left: 0;
}
.websocket-metrics,
@ -680,9 +844,14 @@
flex-wrap: wrap;
}
.connection-status-combined {
width: 100%;
align-items: center;
}
.detailed-status-info {
margin-left: 0;
margin-top: 8px;
margin-top: 0;
justify-content: center;
flex-wrap: wrap;
}

View File

@ -161,6 +161,16 @@ class I18nManager {
}
});
// 翻譯有 data-i18n-title 屬性的元素
const titleElements = document.querySelectorAll('[data-i18n-title]');
titleElements.forEach(element => {
const key = element.getAttribute('data-i18n-title');
const translation = this.t(key);
if (translation && translation !== key) {
element.title = translation;
}
});
// 更新動態內容
this.updateDynamicContent();

View File

@ -36,6 +36,7 @@
this.currentSessionData = null;
this.initializeElements();
this.initializeProjectPathDisplay();
this.startActiveTimeTimer();
console.log('🎨 SessionUIRenderer 初始化完成');
@ -55,6 +56,58 @@
};
};
/**
* 初始化專案路徑顯示
*/
SessionUIRenderer.prototype.initializeProjectPathDisplay = function() {
console.log('🎨 初始化專案路徑顯示');
const projectPathElement = document.getElementById('projectPathDisplay');
console.log('🎨 初始化時找到專案路徑元素:', !!projectPathElement);
if (projectPathElement) {
const fullPath = projectPathElement.getAttribute('data-full-path');
console.log('🎨 初始化時的完整路徑:', fullPath);
if (fullPath) {
// 使用工具函數截斷路徑
const pathResult = window.MCPFeedback.Utils.truncatePathFromRight(fullPath, 2, 40);
console.log('🎨 初始化時路徑處理:', { fullPath, shortPath: pathResult.truncated });
// 更新顯示文字
DOMUtils.safeSetTextContent(projectPathElement, pathResult.truncated);
// 添加點擊複製功能
if (!projectPathElement.hasAttribute('data-copy-handler')) {
console.log('🎨 初始化時添加點擊複製功能');
projectPathElement.setAttribute('data-copy-handler', 'true');
projectPathElement.addEventListener('click', function() {
console.log('🎨 初始化的專案路徑被點擊');
const fullPath = this.getAttribute('data-full-path');
console.log('🎨 初始化時準備複製路徑:', fullPath);
if (fullPath) {
const successMessage = window.i18nManager ?
window.i18nManager.t('app.pathCopied', '專案路徑已複製到剪貼板') :
'專案路徑已複製到剪貼板';
const errorMessage = window.i18nManager ?
window.i18nManager.t('app.pathCopyFailed', '複製路徑失敗') :
'複製路徑失敗';
console.log('🎨 初始化時調用複製函數');
window.MCPFeedback.Utils.copyToClipboard(fullPath, successMessage, errorMessage);
}
});
} else {
console.log('🎨 初始化時點擊複製功能已存在');
}
// 添加 tooltip 位置自動調整
this.adjustTooltipPosition(projectPathElement);
}
}
};
/**
* 渲染當前會話
*/
@ -145,6 +198,88 @@
const projectLabel = window.i18nManager ? window.i18nManager.t('sessionManagement.project') : '專案';
DOMUtils.safeSetTextContent(projectElement, projectLabel + ': ' + projectDir);
}
// 更新頂部狀態列的專案路徑顯示
this.updateTopProjectPathDisplay(sessionData);
};
/**
* 更新頂部狀態列的專案路徑顯示
*/
SessionUIRenderer.prototype.updateTopProjectPathDisplay = function(sessionData) {
console.log('🎨 updateProjectPathDisplay 被調用:', sessionData);
const projectPathElement = document.getElementById('projectPathDisplay');
console.log('🎨 找到專案路徑元素:', !!projectPathElement);
if (projectPathElement && sessionData.project_directory) {
const fullPath = sessionData.project_directory;
// 使用工具函數截斷路徑
const pathResult = window.MCPFeedback.Utils.truncatePathFromRight(fullPath, 2, 40);
console.log('🎨 路徑處理:', { fullPath, shortPath: pathResult.truncated });
// 更新顯示文字
DOMUtils.safeSetTextContent(projectPathElement, pathResult.truncated);
// 更新完整路徑屬性
projectPathElement.setAttribute('data-full-path', fullPath);
// 添加點擊複製功能(如果還沒有)
if (!projectPathElement.hasAttribute('data-copy-handler')) {
console.log('🎨 添加點擊複製功能');
projectPathElement.setAttribute('data-copy-handler', 'true');
projectPathElement.addEventListener('click', function() {
console.log('🎨 專案路徑被點擊');
const fullPath = this.getAttribute('data-full-path');
console.log('🎨 準備複製路徑:', fullPath);
if (fullPath) {
const successMessage = window.i18nManager ?
window.i18nManager.t('app.pathCopied', '專案路徑已複製到剪貼板') :
'專案路徑已複製到剪貼板';
const errorMessage = window.i18nManager ?
window.i18nManager.t('app.pathCopyFailed', '複製路徑失敗') :
'複製路徑失敗';
console.log('🎨 調用複製函數');
window.MCPFeedback.Utils.copyToClipboard(fullPath, successMessage, errorMessage);
}
});
} else {
console.log('🎨 點擊複製功能已存在');
}
// 添加 tooltip 位置自動調整
this.adjustTooltipPosition(projectPathElement);
}
};
/**
* 調整 tooltip 位置以避免超出視窗邊界
*/
SessionUIRenderer.prototype.adjustTooltipPosition = function(element) {
if (!element) return;
// 移除之前的位置類別
element.classList.remove('tooltip-up', 'tooltip-left', 'tooltip-right');
// 獲取元素位置
const rect = element.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// 檢查是否需要調整垂直位置
if (rect.bottom + 100 > viewportHeight) {
element.classList.add('tooltip-up');
}
// 檢查是否需要調整水平位置
if (rect.left + 200 > viewportWidth) {
element.classList.add('tooltip-right');
} else if (rect.left < 200) {
element.classList.add('tooltip-left');
}
};
/**
@ -168,10 +303,30 @@
console.log('🎨 更新會話狀態列:', sessionData);
// 更新當前會話 ID - 顯示完整 session ID
// 更新當前會話 ID - 顯示縮短版本,完整ID存在data-full-id中
const currentSessionElement = document.getElementById('currentSessionId');
if (currentSessionElement && sessionData.session_id) {
DOMUtils.safeSetTextContent(currentSessionElement, sessionData.session_id);
const shortId = sessionData.session_id.substring(0, 8) + '...';
DOMUtils.safeSetTextContent(currentSessionElement, shortId);
currentSessionElement.setAttribute('data-full-id', sessionData.session_id);
// 添加點擊複製功能(如果還沒有)
if (!currentSessionElement.hasAttribute('data-copy-handler')) {
currentSessionElement.setAttribute('data-copy-handler', 'true');
currentSessionElement.addEventListener('click', function() {
const fullId = this.getAttribute('data-full-id');
if (fullId) {
const successMessage = window.i18nManager ?
window.i18nManager.t('app.sessionIdCopied', '會話ID已複製到剪貼板') :
'會話ID已複製到剪貼板';
const errorMessage = window.i18nManager ?
window.i18nManager.t('app.sessionIdCopyFailed', '複製會話ID失敗') :
'複製會話ID失敗';
window.MCPFeedback.Utils.copyToClipboard(fullId, successMessage, errorMessage);
}
});
}
}
// 立即更新活躍時間(定時器會持續更新)

View File

@ -121,6 +121,125 @@
return document.querySelector(selector) !== null;
},
/**
* 從右側截斷路徑保留最後幾個目錄層級
* @param {string} path - 完整路徑
* @param {number} maxLevels - 保留的最大目錄層級數默認2
* @param {number} maxLength - 最大顯示長度默認40
* @returns {object} 包含 truncated截斷後的路徑 isTruncated是否被截斷
*/
truncatePathFromRight: function(path, maxLevels, maxLength) {
maxLevels = maxLevels || 2;
maxLength = maxLength || 40;
if (!path || typeof path !== 'string') {
return { truncated: path || '', isTruncated: false };
}
// 如果路徑長度小於最大長度,直接返回
if (path.length <= maxLength) {
return { truncated: path, isTruncated: false };
}
// 統一路徑分隔符為反斜線Windows風格
const normalizedPath = path.replace(/\//g, '\\');
// 分割路徑
const parts = normalizedPath.split('\\').filter(part => part.length > 0);
if (parts.length <= maxLevels) {
return { truncated: normalizedPath, isTruncated: false };
}
// 取最後幾個層級
const lastParts = parts.slice(-maxLevels);
const truncatedPath = '...' + '\\' + lastParts.join('\\');
return {
truncated: truncatedPath,
isTruncated: true
};
},
/**
* 複製文字到剪貼板統一的複製功能
* @param {string} text - 要複製的文字
* @param {string} successMessage - 成功提示訊息
* @param {string} errorMessage - 錯誤提示訊息
* @returns {Promise<boolean>} 複製是否成功
*/
copyToClipboard: function(text, successMessage, errorMessage) {
successMessage = successMessage || '已複製到剪貼板';
errorMessage = errorMessage || '複製失敗';
return new Promise(function(resolve) {
if (navigator.clipboard && navigator.clipboard.writeText) {
// 使用現代 Clipboard API
navigator.clipboard.writeText(text).then(function() {
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
window.MCPFeedback.Utils.showMessage(successMessage, window.MCPFeedback.Utils.CONSTANTS.MESSAGE_SUCCESS);
}
resolve(true);
}).catch(function(err) {
console.error('Clipboard API 複製失敗:', err);
// 回退到舊方法
const success = window.MCPFeedback.Utils.fallbackCopyToClipboard(text);
if (success) {
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
window.MCPFeedback.Utils.showMessage(successMessage, window.MCPFeedback.Utils.CONSTANTS.MESSAGE_SUCCESS);
}
resolve(true);
} else {
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
window.MCPFeedback.Utils.showMessage(errorMessage, window.MCPFeedback.Utils.CONSTANTS.MESSAGE_ERROR);
}
resolve(false);
}
});
} else {
// 直接使用回退方法
const success = window.MCPFeedback.Utils.fallbackCopyToClipboard(text);
if (success) {
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
window.MCPFeedback.Utils.showMessage(successMessage, window.MCPFeedback.Utils.CONSTANTS.MESSAGE_SUCCESS);
}
resolve(true);
} else {
if (window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.showMessage) {
window.MCPFeedback.Utils.showMessage(errorMessage, window.MCPFeedback.Utils.CONSTANTS.MESSAGE_ERROR);
}
resolve(false);
}
}
});
},
/**
* 回退的複製到剪貼板方法
* @param {string} text - 要複製的文字
* @returns {boolean} 複製是否成功
*/
fallbackCopyToClipboard: function(text) {
try {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
const successful = document.execCommand('copy');
document.body.removeChild(textArea);
return successful;
} catch (err) {
console.error('回退複製方法失敗:', err);
return false;
}
},
/**
* 安全的元素查詢
* @param {string} selector - CSS 選擇器

View File

@ -398,12 +398,32 @@
<h1 data-i18n="app.title">MCP Feedback Enhanced</h1>
</div>
<div class="project-info">
<span data-i18n="app.projectDirectory">專案目錄</span>: {{ project_directory }}
📂 <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 class="connection-status-group">
<!-- 左側:會話狀態資訊 -->
<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;">
<span class="countdown-label" data-i18n="autoSubmit.countdownLabel">提交倒數</span>
@ -424,21 +444,24 @@
</div>
</div>
<!-- 連線詳細資訊 -->
<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 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="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 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>
@ -530,20 +553,6 @@
<!-- ===== 右側主要內容區域 ===== -->
<div class="main-content-area" style="flex: 1; min-width: 0;">
<!-- 會話狀態條 -->
<div class="session-status-bar" style="display: flex; justify-content: space-between; align-items: center; padding: 8px 16px; background: var(--bg-secondary); border-bottom: 1px solid var(--border-color); margin-bottom: 16px; border-radius: 6px;">
<div class="current-session-info">
<span class="session-indicator" style="display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-secondary);">
📋 <span data-i18n="sessionManagement.currentSession">當前會話</span>: <span id="currentSessionId" style="font-family: monospace; color: var(--accent-color);">{{ session_id[:8] if session_id else 'loading' }}...</span>
</span>
<span class="session-age" style="margin-left: 16px; font-size: 12px; color: var(--text-secondary);"><span data-i18n="sessionManagement.activeTime">活躍時間</span>: <span id="sessionAge">--</span></span>
</div>
<div class="session-controls">
<button class="btn-link" id="switchSessionBtn" style="display: none; font-size: 12px; color: var(--accent-color); background: none; border: none; cursor: pointer;" data-i18n="sessionManagement.switchSession">
切換會話
</button>
</div>
</div>
<!-- 分頁導航 -->
<div class="tabs">

View File

@ -0,0 +1,288 @@
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tooltip 方向測試</title>
<style>
:root {
--bg-primary: #1e1e1e;
--bg-secondary: #2d2d30;
--text-primary: #cccccc;
--text-secondary: #969696;
--accent-color: #007acc;
--border-color: #464647;
--transition-fast: 0.2s;
}
body {
background: var(--bg-primary);
color: var(--text-primary);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 20px;
}
.test-container {
max-width: 800px;
margin: 0 auto;
}
.test-section {
margin-bottom: 40px;
padding: 20px;
background: var(--bg-secondary);
border-radius: 8px;
border: 1px solid var(--border-color);
}
.test-header {
background: var(--bg-secondary);
padding: 12px 20px;
border-radius: 8px;
margin-bottom: 20px;
display: flex;
justify-content: center;
align-items: center;
}
/* 會話ID顯示樣式 */
.session-id-display {
font-family: 'Consolas', 'Monaco', monospace;
color: var(--accent-color);
cursor: pointer;
padding: 2px 6px;
border-radius: 4px;
background: rgba(0, 122, 204, 0.1);
border: 1px solid transparent;
transition: all var(--transition-fast) ease;
position: relative;
font-weight: 500;
display: inline-block;
}
.session-id-display:hover {
background: rgba(0, 122, 204, 0.2);
border-color: var(--accent-color);
transform: scale(1.02);
}
.session-id-display:active {
transform: scale(0.98);
}
/* 會話ID tooltip - 向下顯示 */
.session-id-display::after {
content: attr(data-full-id);
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
background: var(--bg-primary);
color: var(--text-primary);
padding: 6px 10px;
border-radius: 6px;
font-size: 11px;
white-space: nowrap;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
border: 1px solid var(--border-color);
opacity: 0;
visibility: hidden;
transition: all var(--transition-fast) ease;
z-index: 1000;
margin-top: 4px;
}
.session-id-display:hover::after {
opacity: 1;
visibility: visible;
}
/* 對比向上顯示的tooltip */
.session-id-display-up {
font-family: 'Consolas', 'Monaco', monospace;
color: var(--accent-color);
cursor: pointer;
padding: 2px 6px;
border-radius: 4px;
background: rgba(0, 122, 204, 0.1);
border: 1px solid transparent;
transition: all var(--transition-fast) ease;
position: relative;
font-weight: 500;
display: inline-block;
}
.session-id-display-up:hover {
background: rgba(0, 122, 204, 0.2);
border-color: var(--accent-color);
transform: scale(1.02);
}
.session-id-display-up::after {
content: attr(data-full-id);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: var(--bg-primary);
color: var(--text-primary);
padding: 6px 10px;
border-radius: 6px;
font-size: 11px;
white-space: nowrap;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
border: 1px solid var(--border-color);
opacity: 0;
visibility: hidden;
transition: all var(--transition-fast) ease;
z-index: 1000;
margin-bottom: 4px;
}
.session-id-display-up:hover::after {
opacity: 1;
visibility: visible;
}
.description {
margin-bottom: 15px;
color: var(--text-secondary);
font-size: 14px;
}
.test-item {
margin: 20px 0;
text-align: center;
}
.test-label {
display: block;
margin-bottom: 10px;
font-weight: 500;
}
.status-message {
margin-top: 20px;
padding: 10px;
border-radius: 4px;
background: rgba(76, 175, 80, 0.2);
color: #4caf50;
border: 1px solid #4caf50;
text-align: center;
}
</style>
</head>
<body>
<div class="test-container">
<h1>Tooltip 方向測試</h1>
<!-- 頂部測試 - 模擬實際使用場景 -->
<div class="test-header">
<span>📋 當前會話:
<span class="session-id-display"
data-full-id="6a674dda-1b98-4f86-824b-afdfd8c581f2"
title="點擊複製完整會話ID">6a674dda...</span>
</span>
</div>
<div class="test-section">
<h3>測試說明</h3>
<div class="description">
以下是不同位置的tooltip顯示測試。將滑鼠懸停在會話ID上查看tooltip的顯示方向。
</div>
<div class="test-item">
<span class="test-label">✅ 新版本 - 向下顯示 (推薦)</span>
<span class="session-id-display"
data-full-id="6a674dda-1b98-4f86-824b-afdfd8c581f2">6a674dda...</span>
</div>
<div class="test-item">
<span class="test-label">❌ 舊版本 - 向上顯示 (會超出畫面)</span>
<span class="session-id-display-up"
data-full-id="6a674dda-1b98-4f86-824b-afdfd8c581f2">6a674dda...</span>
</div>
<div class="status-message">
✅ 新版本的tooltip向下顯示確保在頁面頂部也能完整看到完整的會話ID
</div>
</div>
<div class="test-section">
<h3>實際應用場景</h3>
<div class="description">
在實際應用中會話ID位於頁面頂部的連線狀態欄中。向下顯示tooltip可以確保用戶始終能看到完整的會話ID而不會被瀏覽器視窗邊界截斷。
</div>
<div style="text-align: center; margin-top: 20px;">
<span style="color: var(--text-secondary);">
💡 提示將滑鼠懸停在上方頂部區域的會話ID上測試實際效果
</span>
</div>
</div>
</div>
<script>
// 添加點擊複製功能
document.addEventListener('DOMContentLoaded', function() {
const sessionIdElements = document.querySelectorAll('.session-id-display, .session-id-display-up');
sessionIdElements.forEach(function(element) {
element.addEventListener('click', function() {
const fullId = this.getAttribute('data-full-id');
if (navigator.clipboard) {
navigator.clipboard.writeText(fullId).then(function() {
showMessage('會話ID已複製到剪貼板: ' + fullId);
}).catch(function(err) {
console.error('複製失敗:', err);
fallbackCopy(fullId);
});
} else {
fallbackCopy(fullId);
}
});
});
});
function fallbackCopy(text) {
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showMessage('會話ID已複製到剪貼板: ' + text);
}
function showMessage(message) {
// 創建臨時消息提示
const messageEl = document.createElement('div');
messageEl.textContent = message;
messageEl.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: rgba(76, 175, 80, 0.9);
color: white;
padding: 12px 20px;
border-radius: 6px;
z-index: 10000;
font-size: 14px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
`;
document.body.appendChild(messageEl);
setTimeout(function() {
messageEl.style.opacity = '0';
messageEl.style.transition = 'opacity 0.3s ease';
setTimeout(function() {
document.body.removeChild(messageEl);
}, 300);
}, 2000);
}
</script>
</body>
</html>