mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-24 03:42:31 +02:00
Add files via upload
This commit is contained in:
@@ -457,6 +457,104 @@ body {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
transition: width 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 对话页左侧列表折叠(腾出空间给主对话区) */
|
||||||
|
.conversation-sidebar.collapsed {
|
||||||
|
width: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 对话列表头部:折叠 + 新对话同一行,不重叠 */
|
||||||
|
.conversation-sidebar-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-sidebar-header .new-chat-btn {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
width: auto; /* 覆盖 .new-chat-btn 的 width:100%,让 flex 分配剩余空间 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-sidebar-collapse-btn {
|
||||||
|
/* 不再使用 absolute,避免盖住「新对话」 */
|
||||||
|
position: static;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
min-width: 36px;
|
||||||
|
padding: 0;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
transition: transform 0.2s ease, background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-sidebar-collapse-btn:hover {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 折叠后箭头朝右表示「展开」 */
|
||||||
|
.conversation-sidebar.collapsed .conversation-sidebar-collapse-btn {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-sidebar-collapse-btn svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
stroke: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-sidebar.collapsed .sidebar-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-sidebar.collapsed .conversation-sidebar-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 8px;
|
||||||
|
gap: 10px;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-sidebar.collapsed .conversation-sidebar-collapse-btn {
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-sidebar.collapsed .new-chat-btn {
|
||||||
|
order: 1; /* 窄条:先「+」再折叠,纵向排列 */
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
min-width: 40px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
gap: 0;
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 折叠时只保留「+」,隐藏「新对话」文案 */
|
||||||
|
.conversation-sidebar.collapsed .new-chat-btn span:last-child {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-sidebar.collapsed .new-chat-btn span:first-child {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
@@ -2594,6 +2692,11 @@ header {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 对话列表折叠态在窄屏下仍保持窄条,避免被 240px 覆盖 */
|
||||||
|
.conversation-sidebar.collapsed {
|
||||||
|
width: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-content {
|
.sidebar-content {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@
|
|||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"newChat": "New chat",
|
"newChat": "New chat",
|
||||||
|
"toggleConversationPanel": "Collapse/expand conversation list",
|
||||||
"searchHistory": "Search history...",
|
"searchHistory": "Search history...",
|
||||||
"conversationGroups": "Conversation groups",
|
"conversationGroups": "Conversation groups",
|
||||||
"addGroup": "New group",
|
"addGroup": "New group",
|
||||||
@@ -404,7 +405,12 @@
|
|||||||
"totalCount": "Total",
|
"totalCount": "Total",
|
||||||
"enabledCount": "Enabled",
|
"enabledCount": "Enabled",
|
||||||
"disabledCount": "Disabled",
|
"disabledCount": "Disabled",
|
||||||
"connectedCount": "Connected"
|
"connectedCount": "Connected",
|
||||||
|
"toolsCountValue": "🔧 {{count}} tools",
|
||||||
|
"connectionErrorLabel": "Connection error:",
|
||||||
|
"secondsUnit": "s",
|
||||||
|
"urlLabel": "URL",
|
||||||
|
"loadExternalMCPFailed": "Load failed"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "System settings",
|
"title": "System settings",
|
||||||
|
|||||||
@@ -104,6 +104,7 @@
|
|||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"newChat": "新对话",
|
"newChat": "新对话",
|
||||||
|
"toggleConversationPanel": "折叠/展开对话列表",
|
||||||
"searchHistory": "搜索历史记录...",
|
"searchHistory": "搜索历史记录...",
|
||||||
"conversationGroups": "对话分组",
|
"conversationGroups": "对话分组",
|
||||||
"addGroup": "新建分组",
|
"addGroup": "新建分组",
|
||||||
@@ -404,7 +405,12 @@
|
|||||||
"totalCount": "总数",
|
"totalCount": "总数",
|
||||||
"enabledCount": "已启用",
|
"enabledCount": "已启用",
|
||||||
"disabledCount": "已停用",
|
"disabledCount": "已停用",
|
||||||
"connectedCount": "已连接"
|
"connectedCount": "已连接",
|
||||||
|
"toolsCountValue": "🔧 {{count}} 个工具",
|
||||||
|
"connectionErrorLabel": "连接错误:",
|
||||||
|
"secondsUnit": "秒",
|
||||||
|
"urlLabel": "URL",
|
||||||
|
"loadExternalMCPFailed": "加载失败"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "系统设置",
|
"title": "系统设置",
|
||||||
|
|||||||
+27
-1
@@ -243,7 +243,8 @@ function initPage(pageId) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'chat':
|
case 'chat':
|
||||||
// 对话页面已由chat.js初始化
|
// 恢复对话列表折叠状态(从其他页返回时保持用户选择)
|
||||||
|
initConversationSidebarState();
|
||||||
break;
|
break;
|
||||||
case 'info-collect':
|
case 'info-collect':
|
||||||
// 信息收集页面
|
// 信息收集页面
|
||||||
@@ -421,11 +422,36 @@ function initSidebarState() {
|
|||||||
sidebar.classList.add('collapsed');
|
sidebar.classList.add('collapsed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
initConversationSidebarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换对话页左侧列表折叠/展开
|
||||||
|
function toggleConversationSidebar() {
|
||||||
|
const sidebar = document.getElementById('conversation-sidebar');
|
||||||
|
if (sidebar) {
|
||||||
|
sidebar.classList.toggle('collapsed');
|
||||||
|
const isCollapsed = sidebar.classList.contains('collapsed');
|
||||||
|
localStorage.setItem('conversationSidebarCollapsed', isCollapsed ? 'true' : 'false');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复对话列表折叠状态(进入对话页时生效)
|
||||||
|
function initConversationSidebarState() {
|
||||||
|
const sidebar = document.getElementById('conversation-sidebar');
|
||||||
|
if (sidebar) {
|
||||||
|
const savedState = localStorage.getItem('conversationSidebarCollapsed');
|
||||||
|
if (savedState === 'true') {
|
||||||
|
sidebar.classList.add('collapsed');
|
||||||
|
} else {
|
||||||
|
sidebar.classList.remove('collapsed');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出函数供其他脚本使用
|
// 导出函数供其他脚本使用
|
||||||
window.switchPage = switchPage;
|
window.switchPage = switchPage;
|
||||||
window.toggleSubmenu = toggleSubmenu;
|
window.toggleSubmenu = toggleSubmenu;
|
||||||
window.toggleSidebar = toggleSidebar;
|
window.toggleSidebar = toggleSidebar;
|
||||||
|
window.toggleConversationSidebar = toggleConversationSidebar;
|
||||||
window.currentPage = function() { return currentPage; };
|
window.currentPage = function() { return currentPage; };
|
||||||
|
|
||||||
|
|||||||
@@ -1166,7 +1166,8 @@ async function loadExternalMCPs() {
|
|||||||
console.error('加载外部MCP列表失败:', error);
|
console.error('加载外部MCP列表失败:', error);
|
||||||
const list = document.getElementById('external-mcp-list');
|
const list = document.getElementById('external-mcp-list');
|
||||||
if (list) {
|
if (list) {
|
||||||
list.innerHTML = `<div class="error">加载失败: ${escapeHtml(error.message)}</div>`;
|
const errT = typeof window.t === 'function' ? window.t : (k) => k;
|
||||||
|
list.innerHTML = `<div class="error">${escapeHtml(errT('mcp.loadExternalMCPFailed'))}: ${escapeHtml(error.message)}</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1224,7 +1225,7 @@ function renderExternalMCPList(servers) {
|
|||||||
<div class="external-mcp-item">
|
<div class="external-mcp-item">
|
||||||
<div class="external-mcp-item-header">
|
<div class="external-mcp-item-header">
|
||||||
<div class="external-mcp-item-info">
|
<div class="external-mcp-item-info">
|
||||||
<h4>${transportIcon} ${escapeHtml(name)}${server.tool_count !== undefined && server.tool_count > 0 ? `<span class="tool-count-badge" title="工具数量">🔧 ${server.tool_count}</span>` : ''}</h4>
|
<h4>${transportIcon} ${escapeHtml(name)}${server.tool_count !== undefined && server.tool_count > 0 ? `<span class="tool-count-badge" title="${escapeHtml(statusT('mcp.toolCount'))}">🔧 ${server.tool_count}</span>` : ''}</h4>
|
||||||
<span class="external-mcp-status ${statusClass}">${statusText}</span>
|
<span class="external-mcp-status ${statusClass}">${statusText}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="external-mcp-item-actions">
|
<div class="external-mcp-item-actions">
|
||||||
@@ -1234,7 +1235,7 @@ function renderExternalMCPList(servers) {
|
|||||||
</button>` :
|
</button>` :
|
||||||
status === 'connecting' ?
|
status === 'connecting' ?
|
||||||
`<button class="btn-small" id="btn-toggle-${escapeHtml(name)}" disabled style="opacity: 0.6; cursor: not-allowed;">
|
`<button class="btn-small" id="btn-toggle-${escapeHtml(name)}" disabled style="opacity: 0.6; cursor: not-allowed;">
|
||||||
⏳ 连接中...
|
⏳ ${statusT('mcp.connecting')}
|
||||||
</button>` : ''}
|
</button>` : ''}
|
||||||
<button class="btn-small" onclick="editExternalMCP('${escapeHtml(name)}')" title="${statusT('mcp.editConfig')}" ${status === 'connecting' ? 'disabled' : ''}>✏️ ${statusT('common.edit')}</button>
|
<button class="btn-small" onclick="editExternalMCP('${escapeHtml(name)}')" title="${statusT('mcp.editConfig')}" ${status === 'connecting' ? 'disabled' : ''}>✏️ ${statusT('common.edit')}</button>
|
||||||
<button class="btn-small btn-danger" onclick="deleteExternalMCP('${escapeHtml(name)}')" title="${statusT('mcp.deleteConfig')}" ${status === 'connecting' ? 'disabled' : ''}>🗑 ${statusT('common.delete')}</button>
|
<button class="btn-small btn-danger" onclick="deleteExternalMCP('${escapeHtml(name)}')" title="${statusT('mcp.deleteConfig')}" ${status === 'connecting' ? 'disabled' : ''}>🗑 ${statusT('common.delete')}</button>
|
||||||
@@ -1242,7 +1243,7 @@ function renderExternalMCPList(servers) {
|
|||||||
</div>
|
</div>
|
||||||
${status === 'error' && server.error ? `
|
${status === 'error' && server.error ? `
|
||||||
<div class="external-mcp-error" style="margin: 12px 0; padding: 12px; background: #fee; border-left: 3px solid #f44; border-radius: 4px; color: #c33; font-size: 0.875rem;">
|
<div class="external-mcp-error" style="margin: 12px 0; padding: 12px; background: #fee; border-left: 3px solid #f44; border-radius: 4px; color: #c33; font-size: 0.875rem;">
|
||||||
<strong>❌ 连接错误:</strong>${escapeHtml(server.error)}
|
<strong>❌ ${statusT('mcp.connectionErrorLabel')}</strong>${escapeHtml(server.error)}
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
<div class="external-mcp-item-details">
|
<div class="external-mcp-item-details">
|
||||||
<div>
|
<div>
|
||||||
@@ -1252,7 +1253,7 @@ function renderExternalMCPList(servers) {
|
|||||||
${server.tool_count !== undefined && server.tool_count > 0 ? `
|
${server.tool_count !== undefined && server.tool_count > 0 ? `
|
||||||
<div>
|
<div>
|
||||||
<strong>${statusT('mcp.toolCount')}</strong>
|
<strong>${statusT('mcp.toolCount')}</strong>
|
||||||
<span style="font-weight: 600; color: var(--accent-color);">🔧 ${server.tool_count} 个工具</span>
|
<span style="font-weight: 600; color: var(--accent-color);">${statusT('mcp.toolsCountValue', { count: server.tool_count })}</span>
|
||||||
</div>` : server.tool_count === 0 && status === 'connected' ? `
|
</div>` : server.tool_count === 0 && status === 'connected' ? `
|
||||||
<div>
|
<div>
|
||||||
<strong>${statusT('mcp.toolCount')}</strong>
|
<strong>${statusT('mcp.toolCount')}</strong>
|
||||||
@@ -1266,7 +1267,7 @@ function renderExternalMCPList(servers) {
|
|||||||
${server.config.timeout ? `
|
${server.config.timeout ? `
|
||||||
<div>
|
<div>
|
||||||
<strong>${statusT('mcp.timeout')}</strong>
|
<strong>${statusT('mcp.timeout')}</strong>
|
||||||
<span>${server.config.timeout} 秒</span>
|
<span>${server.config.timeout} ${statusT('mcp.secondsUnit')}</span>
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
${transport === 'stdio' && server.config.command ? `
|
${transport === 'stdio' && server.config.command ? `
|
||||||
<div>
|
<div>
|
||||||
@@ -1275,7 +1276,7 @@ function renderExternalMCPList(servers) {
|
|||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
${transport === 'http' && server.config.url ? `
|
${transport === 'http' && server.config.url ? `
|
||||||
<div>
|
<div>
|
||||||
<strong>URL</strong>
|
<strong>${statusT('mcp.urlLabel')}</strong>
|
||||||
<span style="font-family: monospace; font-size: 0.8125rem; word-break: break-all;">${escapeHtml(server.config.url)}</span>
|
<span style="font-family: monospace; font-size: 0.8125rem; word-break: break-all;">${escapeHtml(server.config.url)}</span>
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
@@ -1327,7 +1328,7 @@ async function editExternalMCP(name) {
|
|||||||
try {
|
try {
|
||||||
const response = await apiFetch(`/api/external-mcp/${encodeURIComponent(name)}`);
|
const response = await apiFetch(`/api/external-mcp/${encodeURIComponent(name)}`);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('获取外部MCP配置失败');
|
throw new Error(typeof window.t === 'function' ? window.t('mcp.getConfigFailed') : '获取外部MCP配置失败');
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = await response.json();
|
const server = await response.json();
|
||||||
@@ -1742,3 +1743,20 @@ openSettings = async function() {
|
|||||||
await originalOpenSettings();
|
await originalOpenSettings();
|
||||||
await loadExternalMCPs();
|
await loadExternalMCPs();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 语言切换后重新渲染 MCP 管理页中由 JS 写入的区块(innerHTML 不会随 data-i18n 自动更新)
|
||||||
|
document.addEventListener('languagechange', function () {
|
||||||
|
try {
|
||||||
|
const mcpPage = document.getElementById('page-mcp-management');
|
||||||
|
if (mcpPage && mcpPage.classList.contains('active')) {
|
||||||
|
if (typeof loadExternalMCPs === 'function') {
|
||||||
|
loadExternalMCPs().catch(function () { /* ignore */ });
|
||||||
|
}
|
||||||
|
if (typeof updateToolsStats === 'function') {
|
||||||
|
updateToolsStats().catch(function () { /* ignore */ });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('languagechange MCP refresh failed', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -400,12 +400,18 @@
|
|||||||
<!-- 对话页面 -->
|
<!-- 对话页面 -->
|
||||||
<div id="page-chat" class="page">
|
<div id="page-chat" class="page">
|
||||||
<div class="chat-page-layout">
|
<div class="chat-page-layout">
|
||||||
<!-- 历史对话侧边栏 -->
|
<!-- 历史对话侧边栏(可折叠,与主导航侧边栏类似) -->
|
||||||
<aside class="conversation-sidebar">
|
<aside class="conversation-sidebar" id="conversation-sidebar">
|
||||||
<div class="sidebar-header">
|
<!-- 头部一行:折叠与「新对话」并排,避免绝对定位重叠(flex 为最佳实践) -->
|
||||||
<button class="new-chat-btn" onclick="startNewConversation()">
|
<div class="sidebar-header conversation-sidebar-header">
|
||||||
|
<button type="button" class="new-chat-btn" onclick="startNewConversation()">
|
||||||
<span>+</span> <span data-i18n="chat.newChat">新对话</span>
|
<span>+</span> <span data-i18n="chat.newChat">新对话</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" class="conversation-sidebar-collapse-btn" onclick="toggleConversationSidebar()" data-i18n="chat.toggleConversationPanel" data-i18n-attr="title" data-i18n-skip-text="true" title="折叠/展开对话列表" aria-label="折叠/展开对话列表">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||||
|
<path d="M15 18l-6-6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<!-- 全局搜索 -->
|
<!-- 全局搜索 -->
|
||||||
|
|||||||
Reference in New Issue
Block a user