From d50fa3d633ec013b91dd5f9f16302c6d723ff214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Sat, 17 Jan 2026 00:07:26 +0800 Subject: [PATCH] Add files via upload --- web/static/css/style.css | 338 ++++++++++++++++++++++++++++++++++++++- web/static/js/roles.js | 150 +++++++++++------ web/static/js/router.js | 18 +++ web/static/js/skills.js | 89 ++++++----- web/templates/index.html | 42 +++-- 5 files changed, 521 insertions(+), 116 deletions(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index 556eb300..8e84ec82 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -3363,7 +3363,7 @@ header { } .pagination-fixed .pagination-info .pagination-page-size select { - padding: 4px 8px; + padding: 4px 24px 4px 8px; border-radius: 6px; border: 1px solid var(--border-color); background: var(--bg-primary); @@ -3371,12 +3371,25 @@ header { font-size: 0.8125rem; cursor: pointer; transition: all 0.2s ease; + width: auto; min-width: 60px; + max-width: 80px; font-weight: 500; /* 更柔和的边框 */ border-color: rgba(233, 236, 239, 0.8); /* 确保四个角都是圆角 */ border-radius: 8px !important; + /* 确保下拉框不被拉伸 */ + flex-shrink: 0; + box-sizing: border-box; + /* 确保下拉箭头正确显示 */ + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 9L1 4h10z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 8px center; + background-size: 12px; } .pagination-fixed .pagination-info .pagination-page-size select:focus { @@ -8600,6 +8613,159 @@ header { gap: 12px; } +/* 角色搜索框样式 */ +.roles-search-box { + position: relative; + margin-bottom: 20px; +} + +.roles-search-box input { + width: 100%; + padding: 10px 40px 10px 16px; + border: 1px solid var(--border-color); + border-radius: 8px; + background: var(--bg-primary); + color: var(--text-primary); + font-size: 0.9375rem; + transition: all 0.2s; +} + +.roles-search-box input:focus { + outline: none; + border-color: var(--accent-color); + box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1); +} + +.roles-search-clear { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + background: none; + border: none; + cursor: pointer; + color: var(--text-secondary); + padding: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: color 0.2s; +} + +.roles-search-clear:hover { + color: var(--text-primary); +} + +/* 角色卡片网格布局 */ +.roles-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 20px; + padding: 0; +} + +/* 角色卡片样式 */ +.role-card { + background: var(--bg-primary); + border: 1px solid var(--border-color); + border-radius: 12px; + padding: 20px; + display: flex; + flex-direction: column; + gap: 12px; + transition: all 0.2s; + cursor: default; +} + +.role-card:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + border-color: var(--accent-color); + transform: translateY(-2px); +} + +.role-card-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 12px; +} + +.role-card-title { + font-size: 1.125rem; + font-weight: 600; + color: var(--text-primary); + margin: 0; + flex: 1; + line-height: 1.4; + display: flex; + align-items: center; + gap: 8px; +} + +.role-card-icon { + font-size: 1.25rem; + line-height: 1; + flex-shrink: 0; +} + +.role-card-badge { + padding: 4px 10px; + border-radius: 12px; + font-size: 0.75rem; + font-weight: 600; + white-space: nowrap; + flex-shrink: 0; +} + +.role-card-badge.enabled { + background: rgba(40, 167, 69, 0.12); + color: var(--success-color); +} + +.role-card-badge.disabled { + background: rgba(220, 53, 69, 0.12); + color: var(--error-color); +} + +.role-card-description { + font-size: 0.875rem; + color: var(--text-secondary); + line-height: 1.5; + flex: 1; + min-height: 40px; +} + +.role-card-tools { + display: flex; + gap: 8px; + font-size: 0.8125rem; + color: var(--text-secondary); + padding-top: 8px; + border-top: 1px solid var(--border-color); +} + +.role-card-tools-label { + font-weight: 500; + white-space: nowrap; +} + +.role-card-tools-value { + color: var(--text-primary); + flex: 1; + word-break: break-word; +} + +.role-card-actions { + display: flex; + gap: 8px; + margin-top: 4px; +} + +.btn-small { + padding: 6px 12px; + font-size: 0.8125rem; +} + .role-item { display: flex; justify-content: space-between; @@ -9251,7 +9417,7 @@ header { /* Skills管理页面样式 */ .skills-controls { - margin-bottom: 16px; + margin-bottom: 8px; } .skills-stats-bar { @@ -9305,6 +9471,174 @@ header { margin-bottom: 16px; } +/* 技能搜索框样式 */ +.skills-search-box { + position: relative; + margin-bottom: 12px; +} + +.skills-search-box input { + width: 100%; + padding: 8px 40px 8px 16px; + border: 1px solid var(--border-color); + border-radius: 8px; + background: var(--bg-primary); + color: var(--text-primary); + font-size: 0.9375rem; + transition: all 0.2s; +} + +.skills-search-box input:focus { + outline: none; + border-color: var(--accent-color); + box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1); +} + +.skills-search-clear { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + background: none; + border: none; + cursor: pointer; + color: var(--text-secondary); + padding: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: color 0.2s; +} + +.skills-search-clear:hover { + color: var(--text-primary); +} + +/* 技能卡片网格布局 */ +.skills-grid { + display: grid; + /* 优化网格布局:使用 auto-fit 让卡片更好地分布,减少最小宽度以容纳更多列 */ + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 16px; + padding: 0; + /* 确保列表可以滚动,不会把分页栏推出视口 */ + flex: 1; + overflow-y: auto; + overflow-x: hidden; + min-height: 0; + /* 统一同一行卡片的高度 */ + align-items: stretch; + /* 让卡片在容器中更好地分布,避免都聚集在左侧 */ + justify-items: stretch; +} + +/* 技能卡片样式 */ +.skill-card { + background: var(--bg-primary); + border: 1px solid var(--border-color); + border-radius: 12px; + padding: 16px; + display: flex; + flex-direction: column; + gap: 10px; + transition: all 0.2s; + cursor: default; + /* 统一卡片高度:让卡片填满网格单元格高度 */ + align-items: stretch; + height: 100%; + /* 确保卡片宽度填满网格单元格 */ + width: 100%; + box-sizing: border-box; +} + +.skill-card:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + border-color: var(--accent-color); + transform: translateY(-2px); +} + +.skill-card-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 12px; +} + +.skill-card-title { + font-size: 1.125rem; + font-weight: 600; + color: var(--text-primary); + margin: 0; + /* 移除 flex: 1,让标题只占据实际需要的高度 */ + line-height: 1.4; + word-break: break-word; + overflow-wrap: break-word; + /* 限制标题最多显示2行 */ + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.skill-card-description { + font-size: 0.875rem; + color: var(--text-secondary); + line-height: 1.6; + /* 让描述区域占据剩余空间,统一卡片高度 */ + flex: 1; + min-height: 48px; + /* 优化文字排版 */ + word-break: break-word; + overflow-wrap: break-word; + /* 限制描述的最大行数,保持卡片整洁 */ + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.skill-card-actions { + display: flex; + gap: 8px; + margin-top: auto; + /* 使用 margin-top: auto 将按钮推到底部,统一卡片布局 */ + flex-shrink: 0; +} + +/* 技能卡片响应式布局优化 */ +@media (min-width: 1400px) { + .skills-grid { + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 20px; + } +} + +@media (max-width: 1200px) { + .skills-grid { + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 16px; + } +} + +@media (max-width: 768px) { + .skills-grid { + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 12px; + } + + .skill-card { + padding: 14px; + gap: 8px; + } +} + +@media (max-width: 480px) { + .skills-grid { + grid-template-columns: 1fr; + gap: 12px; + } +} + .skill-item { background: var(--bg-primary); border: 1px solid var(--border-color); diff --git a/web/static/js/roles.js b/web/static/js/roles.js index deaa5728..5053a948 100644 --- a/web/static/js/roles.js +++ b/web/static/js/roles.js @@ -1,6 +1,8 @@ // 角色管理相关功能 let currentRole = localStorage.getItem('currentRole') || ''; let roles = []; +let rolesSearchKeyword = ''; // 角色搜索关键词 +let rolesSearchTimeout = null; // 搜索防抖定时器 let allRoleTools = []; // 存储所有工具列表(用于角色工具选择) let roleToolsPagination = { page: 1, @@ -258,6 +260,7 @@ async function refreshRoles() { } // 始终更新侧边栏角色选择列表 renderRoleSelectionSidebar(); + showNotification('已刷新', 'success'); } // 渲染角色列表 @@ -265,30 +268,25 @@ function renderRolesList() { const rolesList = document.getElementById('roles-list'); if (!rolesList) return; - if (roles.length === 0) { - rolesList.innerHTML = '
暂无角色
'; + // 过滤角色(根据搜索关键词) + let filteredRoles = roles; + if (rolesSearchKeyword) { + const keyword = rolesSearchKeyword.toLowerCase(); + filteredRoles = roles.filter(role => + role.name.toLowerCase().includes(keyword) || + (role.description && role.description.toLowerCase().includes(keyword)) + ); + } + + if (filteredRoles.length === 0) { + rolesList.innerHTML = '
' + + (rolesSearchKeyword ? '没有找到匹配的角色' : '暂无角色') + + '
'; return; } - // 更新统计 - const stats = document.getElementById('roles-stats'); - if (stats) { - const totalRoles = roles.length; - const enabledRoles = roles.filter(r => r.enabled !== false).length; - stats.innerHTML = ` -
- 总角色数 - ${totalRoles} -
-
- 已启用 - ${enabledRoles} -
- `; - } - // 对角色进行排序:默认角色第一个,其他按名称排序 - const sortedRoles = sortRoles(roles); + const sortedRoles = sortRoles(filteredRoles); rolesList.innerHTML = sortedRoles.map(role => { // 获取角色图标,如果是Unicode转义格式则转换为emoji @@ -307,47 +305,93 @@ function renderRolesList() { } } } + + // 获取工具列表显示 + let toolsDisplay = ''; + let toolsCount = 0; + if (role.name === '默认') { + toolsDisplay = '使用所有工具'; + } else if (role.tools && role.tools.length > 0) { + toolsCount = role.tools.length; + // 显示前5个工具名称 + const toolNames = role.tools.slice(0, 5).map(tool => { + // 如果是外部工具,格式为 external_mcp::tool_name,只显示工具名 + const toolName = tool.includes('::') ? tool.split('::')[1] : tool; + return escapeHtml(toolName); + }); + if (toolsCount <= 5) { + toolsDisplay = toolNames.join(', '); + } else { + toolsDisplay = toolNames.join(', ') + ` 等 ${toolsCount} 个`; + } + } else if (role.mcps && role.mcps.length > 0) { + toolsCount = role.mcps.length; + toolsDisplay = `等 ${toolsCount} 个`; + } else { + toolsDisplay = '使用所有工具'; + } + return ` -
-
-
-
- ${roleIcon} - ${escapeHtml(role.name)} -
-
- ${role.enabled !== false ? '已启用' : '已禁用'} -
-
-
${escapeHtml(role.description || '无描述')}
-
-
- 用户提示词: - ${role.user_prompt ? (role.user_prompt.length > 100 ? escapeHtml(role.user_prompt.substring(0, 100)) + '...' : escapeHtml(role.user_prompt)) : '无'} -
-
- 关联的工具: - ${ - role.name === '默认' - ? '使用所有工具' - : (role.tools && role.tools.length > 0 - ? `${role.tools.length} 个工具` - : (role.mcps && role.mcps.length > 0 - ? `${role.mcps.length} 个工具(兼容旧版)` - : '使用所有工具')) - } -
-
+
+
+

+ ${roleIcon} + ${escapeHtml(role.name)} +

+ + ${role.enabled !== false ? '已启用' : '已禁用'} +
-
- - ${role.name !== '默认' ? `` : ''} +
${escapeHtml(role.description || '无描述')}
+
+ 工具: + ${toolsDisplay} +
+
+ + ${role.name !== '默认' ? `` : ''}
`; }).join(''); } +// 处理角色搜索输入 +function handleRolesSearchInput() { + clearTimeout(rolesSearchTimeout); + rolesSearchTimeout = setTimeout(() => { + searchRoles(); + }, 300); +} + +// 搜索角色 +function searchRoles() { + const searchInput = document.getElementById('roles-search'); + if (!searchInput) return; + + rolesSearchKeyword = searchInput.value.trim(); + const clearBtn = document.getElementById('roles-search-clear'); + if (clearBtn) { + clearBtn.style.display = rolesSearchKeyword ? 'block' : 'none'; + } + + renderRolesList(); +} + +// 清除角色搜索 +function clearRolesSearch() { + const searchInput = document.getElementById('roles-search'); + if (searchInput) { + searchInput.value = ''; + } + rolesSearchKeyword = ''; + const clearBtn = document.getElementById('roles-search-clear'); + if (clearBtn) { + clearBtn.style.display = 'none'; + } + renderRolesList(); +} + // 生成工具唯一标识符(与settings.js中的getToolKey保持一致) function getToolKey(tool) { // 如果是外部工具,使用 external_mcp::tool.name 作为唯一标识符 diff --git a/web/static/js/router.js b/web/static/js/router.js index fd79ccfa..3068ff0f 100644 --- a/web/static/js/router.js +++ b/web/static/js/router.js @@ -280,6 +280,15 @@ function initPage(pageId) { break; case 'roles-management': // 初始化角色管理页面 + // 重置搜索UI(变量会在下次搜索时自动更新) + const rolesSearchInput = document.getElementById('roles-search'); + if (rolesSearchInput) { + rolesSearchInput.value = ''; + } + const rolesSearchClear = document.getElementById('roles-search-clear'); + if (rolesSearchClear) { + rolesSearchClear.style.display = 'none'; + } if (typeof loadRoles === 'function') { loadRoles().then(() => { if (typeof renderRolesList === 'function') { @@ -296,6 +305,15 @@ function initPage(pageId) { break; case 'skills-management': // 初始化Skills管理页面 + // 重置搜索UI(变量会在下次搜索时自动更新) + const skillsSearchInput = document.getElementById('skills-search'); + if (skillsSearchInput) { + skillsSearchInput.value = ''; + } + const skillsSearchClear = document.getElementById('skills-search-clear'); + if (skillsSearchClear) { + skillsSearchClear.style.display = 'none'; + } if (typeof initSkillsPagination === 'function') { initSkillsPagination(); } diff --git a/web/static/js/skills.js b/web/static/js/skills.js index 5b17416d..7de712ea 100644 --- a/web/static/js/skills.js +++ b/web/static/js/skills.js @@ -24,7 +24,7 @@ function getSkillsPageSize() { const saved = localStorage.getItem('skillsPageSize'); if (saved) { const size = parseInt(saved); - if ([10, 20, 50, 100].includes(size)) { + if ([20, 50, 100].includes(size)) { return size; } } @@ -94,7 +94,7 @@ function renderSkillsList() { if (filteredSkills.length === 0) { skillsListEl.innerHTML = '
' + - (skillsSearchKeyword ? '没有找到匹配的skills' : '暂无skills,点击"添加Skill"创建第一个skill') + + (skillsSearchKeyword ? '没有找到匹配的skills' : '暂无skills,点击"创建Skill"创建第一个skill') + '
'; // 搜索时隐藏分页 const paginationContainer = document.getElementById('skills-pagination'); @@ -105,47 +105,31 @@ function renderSkillsList() { } skillsListEl.innerHTML = filteredSkills.map(skill => { - const fileSize = skill.file_size || 0; - const fileSizeStr = fileSize < 1024 ? fileSize + ' B' : - fileSize < 1024 * 1024 ? (fileSize / 1024).toFixed(2) + ' KB' : - (fileSize / (1024 * 1024)).toFixed(2) + ' MB'; - return ` -
-
-
-

${escapeHtml(skill.name || '')}

- ${skill.description ? `

${escapeHtml(skill.description)}

` : ''} -
-
- - - -
+
+
+

${escapeHtml(skill.name || '')}

-
- 路径: ${escapeHtml(skill.path || '')} - 大小: ${fileSizeStr} - ${skill.mod_time ? `修改时间: ${escapeHtml(skill.mod_time)}` : ''} +
${escapeHtml(skill.description || '无描述')}
+
+ + +
`; }).join(''); + + // 确保列表容器可以滚动,分页栏可见 + // 使用 setTimeout 确保 DOM 更新完成后再检查 + setTimeout(() => { + const paginationContainer = document.getElementById('skills-pagination'); + if (paginationContainer && !skillsSearchKeyword) { + // 确保分页栏可见 + paginationContainer.style.display = 'block'; + paginationContainer.style.visibility = 'visible'; + } + }, 0); } // 渲染分页组件(参考MCP管理页面样式) @@ -177,7 +161,6 @@ function renderSkillsPagination() {
-
+
加载中...
@@ -737,23 +736,22 @@

Skills管理

- +
-
-
- 总Skills数 - - -
-
-
- - +
-
+
加载中...