mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 10:42:25 +08:00
🗑️ 刪除舊版相關檔案
This commit is contained in:
parent
a4dd076038
commit
4e1f6c8bb3
@ -1,322 +0,0 @@
|
|||||||
/**
|
|
||||||
* 前端國際化支援 - 新架構版本
|
|
||||||
* =============================
|
|
||||||
*
|
|
||||||
* 提供 Web UI 的多語系支援,支援繁體中文、英文、簡體中文。
|
|
||||||
* 新特性:
|
|
||||||
* - 支援從 API 動態載入翻譯檔案
|
|
||||||
* - 巢狀翻譯鍵值支援
|
|
||||||
* - 舊格式兼容
|
|
||||||
* - 自動偵測瀏覽器語言
|
|
||||||
*/
|
|
||||||
|
|
||||||
class I18nManager {
|
|
||||||
constructor() {
|
|
||||||
this.currentLanguage = null;
|
|
||||||
this.translations = {};
|
|
||||||
this.supportedLanguages = ['zh-TW', 'en', 'zh-CN'];
|
|
||||||
this.fallbackLanguage = 'en';
|
|
||||||
this.isLoaded = false;
|
|
||||||
|
|
||||||
// 內嵌的備用翻譯(防止 API 載入失敗)
|
|
||||||
this.fallbackTranslations = this._getEmbeddedTranslations();
|
|
||||||
|
|
||||||
// 初始化語言設定
|
|
||||||
this.currentLanguage = this.detectLanguage();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 獲取內嵌的備用翻譯(僅保留基本錯誤訊息)
|
|
||||||
*/
|
|
||||||
_getEmbeddedTranslations() {
|
|
||||||
return {
|
|
||||||
'zh-TW': {
|
|
||||||
app: { title: 'Interactive Feedback MCP' },
|
|
||||||
loading: '載入中...',
|
|
||||||
error: '載入失敗',
|
|
||||||
retry: '重試'
|
|
||||||
},
|
|
||||||
'en': {
|
|
||||||
app: { title: 'Interactive Feedback MCP' },
|
|
||||||
loading: 'Loading...',
|
|
||||||
error: 'Loading failed',
|
|
||||||
retry: 'Retry'
|
|
||||||
},
|
|
||||||
'zh-CN': {
|
|
||||||
app: { title: 'Interactive Feedback MCP' },
|
|
||||||
loading: '加载中...',
|
|
||||||
error: '加载失败',
|
|
||||||
retry: '重试'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 從 API 載入翻譯檔案
|
|
||||||
*/
|
|
||||||
async loadTranslations() {
|
|
||||||
try {
|
|
||||||
// 嘗試從 API 載入翻譯
|
|
||||||
const response = await fetch('/api/translations');
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
this.translations = data;
|
|
||||||
this.isLoaded = true;
|
|
||||||
console.log('[I18N] 成功從 API 載入翻譯');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('[I18N] 無法從 API 載入翻譯,使用內嵌翻譯:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用內嵌翻譯作為備用
|
|
||||||
this.translations = this.fallbackTranslations;
|
|
||||||
this.isLoaded = true;
|
|
||||||
console.log('[I18N] 使用內嵌翻譯');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自動偵測語言
|
|
||||||
*/
|
|
||||||
detectLanguage() {
|
|
||||||
// 1. 先檢查 localStorage
|
|
||||||
const savedLang = localStorage.getItem('mcp-feedback-language');
|
|
||||||
if (savedLang && this.supportedLanguages.includes(savedLang)) {
|
|
||||||
return savedLang;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 檢查瀏覽器語言設定
|
|
||||||
const browserLang = navigator.language || navigator.userLanguage;
|
|
||||||
|
|
||||||
// 映射常見的語言代碼
|
|
||||||
const langMap = {
|
|
||||||
'zh-TW': 'zh-TW',
|
|
||||||
'zh-HK': 'zh-TW',
|
|
||||||
'zh-MO': 'zh-TW',
|
|
||||||
'zh-CN': 'zh-CN',
|
|
||||||
'zh-SG': 'zh-CN',
|
|
||||||
'zh': 'zh-TW', // 默認繁體中文
|
|
||||||
'en': 'en',
|
|
||||||
'en-US': 'en',
|
|
||||||
'en-GB': 'en',
|
|
||||||
'en-AU': 'en',
|
|
||||||
'en-CA': 'en'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (langMap[browserLang]) {
|
|
||||||
return langMap[browserLang];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 檢查語言前綴
|
|
||||||
const prefix = browserLang.split('-')[0];
|
|
||||||
if (langMap[prefix]) {
|
|
||||||
return langMap[prefix];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 回退到默認語言
|
|
||||||
return this.fallbackLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 設定語言
|
|
||||||
*/
|
|
||||||
setLanguage(language) {
|
|
||||||
if (!this.supportedLanguages.includes(language)) {
|
|
||||||
console.warn(`Unsupported language: ${language}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentLanguage = language;
|
|
||||||
localStorage.setItem('mcp-feedback-language', language);
|
|
||||||
|
|
||||||
// 觸發語言變更事件
|
|
||||||
document.dispatchEvent(new CustomEvent('languageChanged', {
|
|
||||||
detail: { language: language }
|
|
||||||
}));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 從巢狀物件中獲取值
|
|
||||||
*/
|
|
||||||
_getNestedValue(obj, path) {
|
|
||||||
return path.split('.').reduce((current, key) => {
|
|
||||||
return current && current[key] !== undefined ? current[key] : null;
|
|
||||||
}, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 舊鍵到新鍵的映射
|
|
||||||
*/
|
|
||||||
_getLegacyMapping() {
|
|
||||||
return {
|
|
||||||
// 應用程式標題
|
|
||||||
'app_title': 'app.title',
|
|
||||||
'project_directory': 'app.projectDirectory',
|
|
||||||
'language_selector': 'languageSelector',
|
|
||||||
|
|
||||||
// 語言名稱
|
|
||||||
'lang_zh_tw': 'languageNames.zhTw',
|
|
||||||
'lang_en': 'languageNames.en',
|
|
||||||
'lang_zh_cn': 'languageNames.zhCn',
|
|
||||||
|
|
||||||
// AI 摘要區域
|
|
||||||
'ai_summary': 'aiSummary',
|
|
||||||
|
|
||||||
// 分頁標籤
|
|
||||||
'feedback_tab': 'tabs.feedback',
|
|
||||||
'command_tab': 'tabs.command',
|
|
||||||
|
|
||||||
// 回饋區域
|
|
||||||
'feedback_title': 'feedback.title',
|
|
||||||
'feedback_description': 'feedback.description',
|
|
||||||
'feedback_placeholder': 'feedback.placeholder',
|
|
||||||
|
|
||||||
// 命令區域
|
|
||||||
'command_title': 'command.title',
|
|
||||||
'command_description': 'command.description',
|
|
||||||
'command_placeholder': 'command.placeholder',
|
|
||||||
'command_output': 'command.output',
|
|
||||||
|
|
||||||
// 圖片區域
|
|
||||||
'images_title': 'images.title',
|
|
||||||
'images_select': 'images.select',
|
|
||||||
'images_paste': 'images.paste',
|
|
||||||
'images_clear': 'images.clear',
|
|
||||||
'images_status': 'images.status',
|
|
||||||
'images_status_with_size': 'images.statusWithSize',
|
|
||||||
'images_drag_hint': 'images.dragHint',
|
|
||||||
'images_delete_confirm': 'images.deleteConfirm',
|
|
||||||
'images_delete_title': 'images.deleteTitle',
|
|
||||||
'images_size_warning': 'images.sizeWarning',
|
|
||||||
'images_format_error': 'images.formatError',
|
|
||||||
|
|
||||||
// 按鈕
|
|
||||||
'btn_select_files': 'buttons.selectFiles',
|
|
||||||
'btn_paste_clipboard': 'buttons.pasteClipboard',
|
|
||||||
'btn_clear_all': 'buttons.clearAll',
|
|
||||||
'btn_run_command': 'buttons.runCommand',
|
|
||||||
'btn_submit_feedback': 'buttons.submitFeedback',
|
|
||||||
'btn_cancel': 'buttons.cancel',
|
|
||||||
|
|
||||||
// 狀態消息
|
|
||||||
'uploading': 'status.uploading',
|
|
||||||
'upload_success': 'status.uploadSuccess',
|
|
||||||
'upload_failed': 'status.uploadFailed',
|
|
||||||
'command_running': 'status.commandRunning',
|
|
||||||
'command_finished': 'status.commandFinished',
|
|
||||||
'paste_success': 'status.pasteSuccess',
|
|
||||||
'paste_failed': 'status.pasteFailed',
|
|
||||||
'paste_no_image': 'status.paste_no_image',
|
|
||||||
'paste_image_from_textarea': 'status.paste_image_from_textarea',
|
|
||||||
'invalid_file_type': 'status.invalidFileType',
|
|
||||||
'file_too_large': 'status.fileTooLarge'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 獲取翻譯文字
|
|
||||||
*/
|
|
||||||
t(key, params = {}) {
|
|
||||||
// 確保翻譯已載入
|
|
||||||
if (!this.isLoaded) {
|
|
||||||
// 如果還沒載入,先嘗試從備用翻譯獲取
|
|
||||||
this.translations = this.fallbackTranslations;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentTranslations = this.translations[this.currentLanguage] || {};
|
|
||||||
|
|
||||||
// 嘗試新格式(巢狀鍵)
|
|
||||||
let translation = this._getNestedValue(currentTranslations, key);
|
|
||||||
|
|
||||||
// 如果沒有找到,嘗試舊格式映射
|
|
||||||
if (translation === null) {
|
|
||||||
const legacyMapping = this._getLegacyMapping();
|
|
||||||
const newKey = legacyMapping[key];
|
|
||||||
if (newKey) {
|
|
||||||
translation = this._getNestedValue(currentTranslations, newKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果還是沒有找到,嘗試回退語言
|
|
||||||
if (translation === null) {
|
|
||||||
const fallbackTranslations = this.translations[this.fallbackLanguage] || {};
|
|
||||||
translation = this._getNestedValue(fallbackTranslations, key);
|
|
||||||
|
|
||||||
if (translation === null) {
|
|
||||||
const legacyMapping = this._getLegacyMapping();
|
|
||||||
const newKey = legacyMapping[key];
|
|
||||||
if (newKey) {
|
|
||||||
translation = this._getNestedValue(fallbackTranslations, newKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最後回退到鍵本身
|
|
||||||
if (translation === null) {
|
|
||||||
translation = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 替換參數
|
|
||||||
if (typeof translation === 'string') {
|
|
||||||
translation = translation.replace(/{(\w+)}/g, (match, param) => {
|
|
||||||
return params[param] !== undefined ? params[param] : match;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return translation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 獲取語言顯示名稱
|
|
||||||
*/
|
|
||||||
getLanguageDisplayName(languageCode) {
|
|
||||||
const key = `languageNames.${languageCode.toLowerCase().replace('-', '')}`;
|
|
||||||
if (languageCode === 'zh-TW') {
|
|
||||||
return this.t('languageNames.zhTw');
|
|
||||||
} else if (languageCode === 'zh-CN') {
|
|
||||||
return this.t('languageNames.zhCn');
|
|
||||||
} else if (languageCode === 'en') {
|
|
||||||
return this.t('languageNames.en');
|
|
||||||
}
|
|
||||||
return this.t(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 獲取當前語言
|
|
||||||
*/
|
|
||||||
getCurrentLanguage() {
|
|
||||||
return this.currentLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 獲取支援的語言列表
|
|
||||||
*/
|
|
||||||
getSupportedLanguages() {
|
|
||||||
return [...this.supportedLanguages];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化(載入翻譯)
|
|
||||||
*/
|
|
||||||
async init() {
|
|
||||||
await this.loadTranslations();
|
|
||||||
return this.isLoaded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 創建全域實例
|
|
||||||
window.i18n = new I18nManager();
|
|
||||||
|
|
||||||
// 翻譯函數的全域快捷方式
|
|
||||||
window.t = function(key, params = {}) {
|
|
||||||
return window.i18n.t(key, params);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 初始化函數
|
|
||||||
window.initI18n = async function() {
|
|
||||||
await window.i18n.init();
|
|
||||||
return window.i18n.isLoaded;
|
|
||||||
};
|
|
@ -1,447 +0,0 @@
|
|||||||
/* Interactive Feedback MCP - Modern Dark Theme */
|
|
||||||
:root {
|
|
||||||
--primary-color: #007acc;
|
|
||||||
--primary-hover: #005999;
|
|
||||||
--background-color: #1e1e1e;
|
|
||||||
--surface-color: #2d2d30;
|
|
||||||
--surface-hover: #383838;
|
|
||||||
--text-primary: #cccccc;
|
|
||||||
--text-secondary: #9e9e9e;
|
|
||||||
--text-accent: #007acc;
|
|
||||||
--border-color: #464647;
|
|
||||||
--success-color: #4caf50;
|
|
||||||
--warning-color: #ff9800;
|
|
||||||
--error-color: #f44336;
|
|
||||||
--console-bg: #1a1a1a;
|
|
||||||
--input-bg: #2d2d30;
|
|
||||||
--button-bg: #0e639c;
|
|
||||||
--button-hover-bg: #1177bb;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
background-color: var(--background-color);
|
|
||||||
color: var(--text-primary);
|
|
||||||
line-height: 1.6;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
color: var(--text-accent);
|
|
||||||
margin-bottom: 30px;
|
|
||||||
font-size: 2.5em;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2, h3 {
|
|
||||||
color: var(--text-primary);
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.3em;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
background-color: var(--surface-color);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section:hover {
|
|
||||||
background-color: var(--surface-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-info {
|
|
||||||
background: linear-gradient(135deg, var(--surface-color), var(--surface-hover));
|
|
||||||
border-left: 4px solid var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-info p {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-info strong {
|
|
||||||
color: var(--text-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-btn {
|
|
||||||
width: 100%;
|
|
||||||
background-color: var(--button-bg);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 12px 20px;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 1.1em;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-btn:hover {
|
|
||||||
background-color: var(--button-hover-bg);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.command-section {
|
|
||||||
animation: slideDown 0.3s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideDown {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-10px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group input {
|
|
||||||
flex: 1;
|
|
||||||
background-color: var(--input-bg);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 12px 15px;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 14px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--primary-color);
|
|
||||||
box-shadow: 0 0 0 3px rgba(0, 122, 204, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group button {
|
|
||||||
background-color: var(--button-bg);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 12px 20px;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
min-width: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group button:hover {
|
|
||||||
background-color: var(--button-hover-bg);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
|
|
||||||
#stop-btn {
|
|
||||||
background-color: var(--error-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#stop-btn:hover {
|
|
||||||
background-color: #d32f2f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 15px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group input[type="checkbox"] {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
accent-color: var(--primary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#save-config {
|
|
||||||
background-color: var(--success-color);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 13px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#save-config:hover {
|
|
||||||
background-color: #45a049;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-section {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-header h4 {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
#clear-logs {
|
|
||||||
background-color: var(--warning-color);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 6px 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 12px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#clear-logs:hover {
|
|
||||||
background-color: #f57c00;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console {
|
|
||||||
background-color: var(--console-bg);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 15px;
|
|
||||||
height: 300px;
|
|
||||||
overflow-y: auto;
|
|
||||||
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.4;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console-line {
|
|
||||||
margin-bottom: 2px;
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.console::-webkit-scrollbar {
|
|
||||||
width: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console::-webkit-scrollbar-track {
|
|
||||||
background: var(--surface-color);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console::-webkit-scrollbar-thumb {
|
|
||||||
background: var(--border-color);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.feedback-section {
|
|
||||||
background: linear-gradient(135deg, var(--surface-color), var(--surface-hover));
|
|
||||||
border-left: 4px solid var(--success-color);
|
|
||||||
flex-grow: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feedback-description {
|
|
||||||
background-color: var(--input-bg);
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
border-left: 3px solid var(--primary-color);
|
|
||||||
font-style: italic;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
#feedback-input {
|
|
||||||
flex-grow: 1;
|
|
||||||
min-height: 150px;
|
|
||||||
background-color: var(--input-bg);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 15px;
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 14px;
|
|
||||||
font-family: inherit;
|
|
||||||
resize: vertical;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#feedback-input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--primary-color);
|
|
||||||
box-shadow: 0 0 0 3px rgba(0, 122, 204, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#feedback-input::placeholder {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
#submit-feedback {
|
|
||||||
background: linear-gradient(135deg, var(--success-color), #66bb6a);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 15px 30px;
|
|
||||||
border-radius: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#submit-feedback:hover {
|
|
||||||
background: linear-gradient(135deg, #45a049, #4caf50);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 30px;
|
|
||||||
padding: 20px;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: 13px;
|
|
||||||
border-top: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer a {
|
|
||||||
color: var(--text-accent);
|
|
||||||
text-decoration: none;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer a:hover {
|
|
||||||
color: var(--primary-hover);
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
border: 4px solid var(--border-color);
|
|
||||||
border-top: 4px solid var(--primary-color);
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading p {
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive Design */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.container {
|
|
||||||
padding: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2em;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group button {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console {
|
|
||||||
height: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#feedback-input {
|
|
||||||
min-height: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Smooth transitions for all interactive elements */
|
|
||||||
* {
|
|
||||||
transition: color 0.3s ease, background-color 0.3s ease, border-color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Focus styles for accessibility */
|
|
||||||
button:focus,
|
|
||||||
input:focus,
|
|
||||||
textarea:focus {
|
|
||||||
outline: 2px solid var(--primary-color);
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom selection colors */
|
|
||||||
::selection {
|
|
||||||
background-color: var(--primary-color);
|
|
||||||
color: white;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,41 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-TW">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Interactive Feedback MCP Server</title>
|
|
||||||
<link rel="stylesheet" href="/static/style.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1>Interactive Feedback MCP</h1>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<h2>服務器狀態</h2>
|
|
||||||
<p>🟢 MCP 服務器正在運行</p>
|
|
||||||
<p>等待來自 AI 助手的互動請求...</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<h3>關於此服務</h3>
|
|
||||||
<p>這是一個 Model Context Protocol (MCP) 服務器,用於在 AI 輔助開發工具中提供人在回路的互動回饋功能。</p>
|
|
||||||
<p>當 AI 助手需要用戶回饋時,會自動在瀏覽器中開啟互動頁面。</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="section">
|
|
||||||
<h3>功能特色</h3>
|
|
||||||
<ul style="color: var(--text-primary); margin-left: 20px;">
|
|
||||||
<li>🌐 Web UI 支援 SSH remote 開發</li>
|
|
||||||
<li>💻 即時命令執行和輸出顯示</li>
|
|
||||||
<li>💬 結構化回饋收集</li>
|
|
||||||
<li>⚙️ 專案特定的設定管理</li>
|
|
||||||
<li>🔄 WebSocket 即時通訊</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer">
|
|
||||||
<p>開發者: Fábio Ferreira | <a href="https://x.com/fabiomlferreira" target="_blank">X.com</a> | <a href="https://dotcursorrules.com/" target="_blank">dotcursorrules.com</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
x
Reference in New Issue
Block a user