diff --git a/web/static/css/style.css b/web/static/css/style.css index 4441cbbd..134b6e4b 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -9289,6 +9289,7 @@ header { margin-bottom: 0; display: -webkit-box; -webkit-line-clamp: 2; + line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } @@ -13231,6 +13232,9 @@ header { font-variant-numeric: tabular-nums; white-space: nowrap; flex-shrink: 0; + justify-self: end; + text-align: right; + min-width: 3.5rem; } /* External MCP 健康度:能力总览中专门一行的 N/N + 状态徽章 */ diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index 78326120..71ddf2a5 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -125,7 +125,7 @@ "lastUpdated": "Last updated", "viewAll": "View all →", "recentVulns": "Recent vulnerabilities", - "noVulnYet": "No vulnerabilities yet — start your first scan", + "noVulnYet": "No recent vulnerabilities", "capabilities": "Capabilities", "mcpTools": "MCP tools", "rolesLabel": "Roles", @@ -184,7 +184,7 @@ "recoCheckMonitorDesc": "View failed request details in MCP monitor", "recoSetupMcp": "Configure your first MCP tool", "recoSetupMcpDesc": "Install MCP server before Agent can invoke specific capabilities", - "recoStartScan": "Start your first scan", + "recoStartScan": "Start a scan from chat", "recoStartScanDesc": "Describe your target in chat, AI will help execute", "recentEvents": "Recent Events", "eventUntitled": "Event", @@ -193,7 +193,7 @@ "mcpPartialDown_one": "{{count}} stopped", "mcpPartialDown_other": "{{count}} stopped", "mcpAllDown": "All stopped", - "noVulnDesc": "System looks safe — start a scan to discover potential issues", + "noVulnDesc": "This list shows recent records; new results appear here when detection completes in chat", "startScanBtn": "Go to chat to scan" }, "chat": { diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index 335d22af..6d447c1a 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -125,7 +125,7 @@ "lastUpdated": "上次更新", "viewAll": "查看全部 →", "recentVulns": "最近漏洞", - "noVulnYet": "暂无漏洞,开始你的第一次扫描吧", + "noVulnYet": "暂无最近漏洞", "capabilities": "能力总览", "mcpTools": "MCP 工具", "rolesLabel": "角色", @@ -174,7 +174,7 @@ "recoCheckMonitorDesc": "在 MCP 监控中查看失败的请求详情", "recoSetupMcp": "配置首个 MCP 工具", "recoSetupMcpDesc": "安装 MCP 服务后 Agent 才能调用具体能力", - "recoStartScan": "开始第一次扫描", + "recoStartScan": "在对话中发起扫描", "recoStartScanDesc": "在对话中描述目标,让 AI 协助执行", "recentEvents": "最近事件", "eventUntitled": "事件", @@ -182,7 +182,7 @@ "mcpAllRunning": "全部运行", "mcpPartialDown": "{{count}} 个未运行", "mcpAllDown": "全部未运行", - "noVulnDesc": "系统目前安全,开始一次扫描可以发现潜在问题", + "noVulnDesc": "此处展示近期漏洞记录;在对话中完成检测后,新结果会出现在这里", "startScanBtn": "前往对话发起扫描" }, "chat": { diff --git a/web/static/js/dashboard.js b/web/static/js/dashboard.js index a312cce5..3f39184b 100644 --- a/web/static/js/dashboard.js +++ b/web/static/js/dashboard.js @@ -662,8 +662,8 @@ function getHitlPendingCount(res) { // 「最近事件」内联展示:取通知摘要里最重要的前 N 条 // 设计原则: -// - 不重复 alert banner / KPI 已经表达过的信息(漏洞、HITL 等会被过滤掉避免冗余) -// - 只显示 p0/p1 优先级,p2 作为兜底(当 p0/p1 不够时) +// - 不重复 alert banner / KPI 已表达的「新漏洞」通知(vulnerability_created 仍过滤) +// - HITL 待审批在推荐操作等处也会提示,但仍在此展示时间线,便于与任务完成等并列查看 // - 整个 section 在没有可显示内容时整个隐藏,避免空模块占地方 function renderRecentEvents(notifRes) { var section = document.getElementById('dashboard-section-events'); @@ -671,8 +671,8 @@ function renderRecentEvents(notifRes) { if (!section || !listEl) return; var items = (notifRes && Array.isArray(notifRes.items)) ? notifRes.items : []; - // 过滤:只看有意义的事件,去掉 actionable 已处理的、以及类型已经在仪表盘其他位置覆盖的 - var coveredTypes = { 'vulnerability_created': true, 'hitl_pending': true }; + // 过滤:去掉新漏洞类型(与「最近漏洞」等板块避免重复);HITL 不再过滤 + var coveredTypes = { 'vulnerability_created': true }; var filtered = items.filter(function (it) { if (!it || !it.type) return false; if (coveredTypes[it.type]) return false; @@ -685,8 +685,8 @@ function renderRecentEvents(notifRes) { var la = levelOrder[a.level] != null ? levelOrder[a.level] : 9; var lb = levelOrder[b.level] != null ? levelOrder[b.level] : 9; if (la !== lb) return la - lb; - var ta = a.createdAt || a.created_at || 0; - var tb = b.createdAt || b.created_at || 0; + var ta = a.ts || a.createdAt || a.created_at || 0; + var tb = b.ts || b.createdAt || b.created_at || 0; return new Date(tb).getTime() - new Date(ta).getTime(); }); @@ -701,8 +701,9 @@ function renderRecentEvents(notifRes) { listEl.innerHTML = top.map(function (it) { var level = it.level || 'p2'; var title = esc(it.title || it.message || dt('dashboard.eventUntitled', null, '事件')); - var msg = esc(it.message || it.summary || ''); - var when = esc(timeAgoStr(it.createdAt || it.created_at)); + var msg = esc(it.message || it.summary || it.desc || ''); + var whenRaw = timeAgoStr(it.ts || it.createdAt || it.created_at); + var when = esc(whenRaw || '—'); return ( '
' + '' + @@ -784,7 +785,7 @@ function renderRecommendedActions(state) { actions.push({ level: 'setup', icon: '', - title: dt('dashboard.recoStartScan', null, '开始第一次扫描'), + title: dt('dashboard.recoStartScan', null, '在对话中发起扫描'), desc: dt('dashboard.recoStartScanDesc', null, '在对话中描述目标,让 AI 协助执行'), page: 'chat' }); @@ -972,8 +973,8 @@ function renderRecentVulns(res) { '' + - '
' + esc(dt('dashboard.noVulnYet', null, '暂无漏洞')) + '
' + - '
' + esc(dt('dashboard.noVulnDesc', null, '系统目前安全,开始一次扫描可以发现潜在问题')) + '
' + + '
' + esc(dt('dashboard.noVulnYet', null, '暂无最近漏洞')) + '
' + + '
' + esc(dt('dashboard.noVulnDesc', null, '此处展示近期漏洞记录;在对话中完成检测后,新结果会出现在这里')) + '
' + '' ); diff --git a/web/static/js/roles.js b/web/static/js/roles.js index 925b6a88..0aec161b 100644 --- a/web/static/js/roles.js +++ b/web/static/js/roles.js @@ -1,6 +1,28 @@ // 角色管理相关功能 function _t(key, opts) { - return typeof window.t === 'function' ? window.t(key, opts) : key; + if (typeof window.t === 'function') { + try { + var translated = window.t(key, opts); + if (typeof translated === 'string' && translated && translated !== key) { + return translated; + } + } catch (e) { /* ignore */ } + } + // i18n 未就绪或词条缺失时避免把 key 暴露给用户(与 zh-CN 默认一致) + if (key === 'roles.noDescription') return '暂无描述'; + if (key === 'roles.noDescriptionShort') return '无描述'; + if (key === 'roles.defaultRoleDescription') { + return '默认角色,不额外携带用户提示词,使用默认MCP'; + } + return key; +} + +/** 角色配置中的描述:trim,并把误存为 i18n key 的字面量视为空 */ +function rolePlainDescription(role) { + const raw = typeof role.description === 'string' ? role.description.trim() : ''; + if (!raw) return ''; + if (raw === 'roles.noDescription' || raw === 'roles.noDescriptionShort') return ''; + return raw; } let currentRole = localStorage.getItem('currentRole') || ''; let roles = []; @@ -56,6 +78,11 @@ function sortRoles(rolesArray) { // 加载所有角色 async function loadRoles() { + if (window.i18nReady && typeof window.i18nReady.then === 'function') { + try { + await window.i18nReady; + } catch (e) { /* ignore */ } + } try { const response = await apiFetch('/api/roles'); if (!response.ok) { @@ -189,8 +216,9 @@ function renderRoleSelectionSidebar() { const icon = getRoleIcon(role); // 处理默认角色的描述 - let description = role.description || _t('roles.noDescription'); - if (isDefaultRole && !role.description) { + const plainDesc = rolePlainDescription(role); + let description = plainDesc || _t('roles.noDescription'); + if (isDefaultRole && !plainDesc) { description = _t('roles.defaultRoleDescription'); } @@ -316,6 +344,7 @@ function renderRolesList() { const sortedRoles = sortRoles(filteredRoles); rolesList.innerHTML = sortedRoles.map(role => { + const plainDesc = rolePlainDescription(role); // 获取角色图标,如果是Unicode转义格式则转换为emoji let roleIcon = role.icon || '👤'; if (roleIcon && typeof roleIcon === 'string') { @@ -369,7 +398,7 @@ function renderRolesList() { ${role.enabled !== false ? _t('roles.enabled') : _t('roles.disabled')}
-
${escapeHtml(role.description || _t('roles.noDescriptionShort'))}
+
${escapeHtml(plainDesc || _t('roles.noDescriptionShort'))}
${_t('roleModal.toolsLabel')} ${toolsDisplay} @@ -1575,9 +1604,10 @@ document.addEventListener('DOMContentLoaded', () => { updateRoleSelectorDisplay(); }); -// 语言切换后刷新角色选择器显示(默认/自定义角色名) +// 语言切换后刷新角色选择器与「选择角色」列表文案 document.addEventListener('languagechange', () => { updateRoleSelectorDisplay(); + renderRoleSelectionSidebar(); }); // 获取当前选中的角色(供chat.js使用) diff --git a/web/templates/index.html b/web/templates/index.html index 58385b28..c33e9700 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -498,7 +498,7 @@ 查看全部 →
-
暂无漏洞,开始你的第一次扫描吧
+
暂无最近漏洞