Add files via upload

This commit is contained in:
公明
2026-07-06 16:50:05 +08:00
committed by GitHub
parent c107c77619
commit 00a936e5dc
5 changed files with 290 additions and 16 deletions
+5 -1
View File
@@ -2152,13 +2152,17 @@ function appendChatProjectPanelItem(list, project, selectedId, onSelect, tFn) {
const t = tFn || tp;
const isNone = !project.id;
const isSelected = isNone ? !selectedId : selectedId === project.id;
const fullDesc = isNone
? (project.description || '')
: (project.description || '').trim() || t('projects.sharedFactBoard');
const desc = isNone
? (project.description || '')
: (project.description || '').trim().slice(0, 80) || t('projects.sharedFactBoard');
: fullDesc.slice(0, 80);
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'role-selection-item-main' + (isSelected ? ' selected' : '');
btn.setAttribute('role', 'option');
btn.setAttribute('data-selection-detail', fullDesc);
btn.onclick = () => onSelect(project.id || '');
btn.innerHTML = `
<div class="role-selection-item-icon-main">${isNone ? '—' : '📁'}</div>
+82
View File
@@ -24,6 +24,79 @@ function rolePlainDescription(role) {
if (raw === 'roles.noDescription' || raw === 'roles.noDescriptionShort') return '';
return raw;
}
function initSelectionDetailTooltip() {
if (window.__selectionDetailTooltipReady) return;
window.__selectionDetailTooltipReady = true;
const tooltip = document.createElement('div');
tooltip.className = 'selection-detail-tooltip';
tooltip.setAttribute('role', 'tooltip');
document.body.appendChild(tooltip);
let activeEl = null;
function hideTooltip() {
activeEl = null;
tooltip.classList.remove('visible', 'placement-left');
}
function positionTooltip(el) {
const text = el && el.getAttribute('data-selection-detail');
if (!text) {
hideTooltip();
return;
}
activeEl = el;
tooltip.textContent = text;
tooltip.classList.add('visible');
const rect = el.getBoundingClientRect();
const tipRect = tooltip.getBoundingClientRect();
const gap = 12;
const margin = 12;
const rightSpace = window.innerWidth - rect.right - gap - margin;
const placeLeft = rightSpace < tipRect.width && rect.left > tipRect.width + gap + margin;
const left = placeLeft ? rect.left - tipRect.width - gap : rect.right + gap;
const top = Math.max(margin, Math.min(rect.top + rect.height / 2 - tipRect.height / 2, window.innerHeight - tipRect.height - margin));
tooltip.classList.toggle('placement-left', placeLeft);
tooltip.style.left = left + 'px';
tooltip.style.top = top + 'px';
}
document.addEventListener('mouseover', function (event) {
const el = event.target && event.target.closest && event.target.closest('[data-selection-detail]');
if (!el || (event.relatedTarget && el.contains(event.relatedTarget))) return;
positionTooltip(el);
});
document.addEventListener('mouseout', function (event) {
const el = event.target && event.target.closest && event.target.closest('[data-selection-detail]');
if (!el || (event.relatedTarget && el.contains(event.relatedTarget))) return;
hideTooltip();
});
document.addEventListener('focusin', function (event) {
const el = event.target && event.target.closest && event.target.closest('[data-selection-detail]');
if (el) positionTooltip(el);
});
document.addEventListener('focusout', function (event) {
const el = event.target && event.target.closest && event.target.closest('[data-selection-detail]');
if (el) hideTooltip();
});
window.addEventListener('scroll', hideTooltip, true);
window.addEventListener('resize', function () {
if (activeEl) positionTooltip(activeEl);
});
}
if (typeof document !== 'undefined') {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSelectionDetailTooltip);
} else {
initSelectionDetailTooltip();
}
}
let currentRole = localStorage.getItem('currentRole') || '';
let roles = [];
let rolesSearchKeyword = ''; // 角色搜索关键词
@@ -209,10 +282,18 @@ function renderRoleSelectionSidebar() {
const isSelected = isDefaultRole ? (currentRole === '' || currentRole === '默认') : (currentRole === role.name);
const roleItem = document.createElement('div');
roleItem.className = 'role-selection-item-main' + (isSelected ? ' selected' : '');
roleItem.setAttribute('role', 'option');
roleItem.tabIndex = 0;
roleItem.onclick = () => {
selectRole(role.name);
closeRoleSelectionPanel(); // 选择后自动关闭面板
};
roleItem.onkeydown = (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
roleItem.click();
}
};
const icon = getRoleIcon(role);
// 处理默认角色的描述
@@ -221,6 +302,7 @@ function renderRoleSelectionSidebar() {
if (isDefaultRole && !plainDesc) {
description = _t('roles.defaultRoleDescription');
}
roleItem.setAttribute('data-selection-detail', description);
roleItem.innerHTML = `
<div class="role-selection-item-icon-main">${icon}</div>
+16 -10
View File
@@ -223,12 +223,13 @@ function wsRenderRoleList() {
var html = '';
// 默认角色
var defSelected = !cur ? ' selected' : '';
html += '<button type="button" class="role-selection-item-main' + defSelected + '" onclick="wsSelectRole(\'\')">' +
var defDesc = wsTOr('roles.defaultRoleDescription', '默认角色,不额外携带用户提示词,使用所有工具');
html += '<button type="button" class="role-selection-item-main' + defSelected + '" data-selection-detail="' + escapeHtmlAttr(defDesc) + '" onclick="wsSelectRole(\'\')">' +
'<div class="role-selection-item-icon-main">\ud83d\udd35</div>' +
'<div class="role-selection-item-content-main"><div class="role-selection-item-name-main">' +
(wsTOr('chat.defaultRole', '默认')) +
'</div><div class="role-selection-item-description-main">' +
(wsTOr('roles.defaultRoleDescription', '默认角色,不额外携带用户提示词,使用所有工具')) +
escapeHtml(defDesc) +
'</div></div>' +
(defSelected ? '<div class="role-selection-checkmark-main">\u2713</div>' : '') +
'</button>';
@@ -238,10 +239,11 @@ function wsRenderRoleList() {
if (!r.enabled) continue;
if (r.name === '默认') continue; // 已在上方硬编码默认角色,跳过 API 返回的默认项
var sel = (r.name === cur) ? ' selected' : '';
html += '<button type="button" class="role-selection-item-main' + sel + '" onclick="wsSelectRole(\'' + r.name.replace(/'/g, "\\'") + '\')">' +
'<div class="role-selection-item-icon-main">' + (r.icon || '\ud83d\udd35') + '</div>' +
'<div class="role-selection-item-content-main"><div class="role-selection-item-name-main">' + r.name + '</div>' +
'<div class="role-selection-item-description-main">' + (r.description || '').substring(0, 60) + '</div></div>' +
var desc = r.description || '';
html += '<button type="button" class="role-selection-item-main' + sel + '" data-selection-detail="' + escapeHtmlAttr(desc) + '" onclick="wsSelectRole(\'' + r.name.replace(/'/g, "\\'") + '\')">' +
'<div class="role-selection-item-icon-main">' + escapeHtml(r.icon || '\ud83d\udd35') + '</div>' +
'<div class="role-selection-item-content-main"><div class="role-selection-item-name-main">' + escapeHtml(r.name) + '</div>' +
'<div class="role-selection-item-description-main">' + escapeHtml(desc.substring(0, 60)) + '</div></div>' +
(sel ? '<div class="role-selection-checkmark-main">\u2713</div>' : '') +
'</button>';
}
@@ -1136,6 +1138,10 @@ function escapeHtml(s) {
return div.innerHTML;
}
function escapeHtmlAttr(s) {
return escapeHtml(s).replace(/"/g, '&quot;').replace(/'/g, '&#39;');
}
function escapeSingleQuotedShellArg(value) {
var s = value == null ? '' : String(value);
return "'" + s.replace(/'/g, "'\\''") + "'";
@@ -2260,10 +2266,10 @@ function selectWebshell(id, stateReady) {
'<button type="button" class="role-selection-panel-close" onclick="wsCloseAgentModePanel()"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button>' +
'</div>' +
'<div class="agent-mode-options">' +
'<button type="button" class="role-selection-item-main agent-mode-option ws-agent-mode-option" data-value="eino_single" role="option" onclick="wsSelectAgentMode(\'eino_single\')"><div class="role-selection-item-icon-main">\u26a1</div><div class="role-selection-item-content-main"><div class="role-selection-item-name-main">' + (wsT('chat.agentModeEinoSingle') || 'Eino 单代理(ADK') + '</div><div class="role-selection-item-description-main">' + (wsT('chat.agentModeEinoSingleHint') || 'Eino ChatModelAgent + Runner') + '</div></div><div class="role-selection-checkmark-main agent-mode-check" data-agent-mode-check="eino_single">\u2713</div></button>' +
'<button type="button" class="role-selection-item-main agent-mode-option ws-agent-mode-option" data-value="deep" role="option" onclick="wsSelectAgentMode(\'deep\')"><div class="role-selection-item-icon-main">\ud83e\udde9</div><div class="role-selection-item-content-main"><div class="role-selection-item-name-main">' + (wsT('chat.agentModeDeep') || 'DeepDeepAgent') + '</div><div class="role-selection-item-description-main">' + (wsT('chat.agentModeDeepHint') || 'Eino DeepAgent,适合复杂安全测试、多阶段 task 子代理委派与汇总') + '</div></div><div class="role-selection-checkmark-main agent-mode-check" data-agent-mode-check="deep">\u2713</div></button>' +
'<button type="button" class="role-selection-item-main agent-mode-option ws-agent-mode-option" data-value="plan_execute" role="option" onclick="wsSelectAgentMode(\'plan_execute\')"><div class="role-selection-item-icon-main">\ud83d\udccb</div><div class="role-selection-item-content-main"><div class="role-selection-item-name-main">' + (wsT('chat.agentModePlanExecuteLabel') || 'Plan-Execute') + '</div><div class="role-selection-item-description-main">' + (wsT('chat.agentModePlanExecuteHint') || '规划 → 执行 → 重规划') + '</div></div><div class="role-selection-checkmark-main agent-mode-check" data-agent-mode-check="plan_execute">\u2713</div></button>' +
'<button type="button" class="role-selection-item-main agent-mode-option ws-agent-mode-option" data-value="supervisor" role="option" onclick="wsSelectAgentMode(\'supervisor\')"><div class="role-selection-item-icon-main">\ud83c\udfaf</div><div class="role-selection-item-content-main"><div class="role-selection-item-name-main">' + (wsT('chat.agentModeSupervisorLabel') || 'Supervisor(专家路由)') + '</div><div class="role-selection-item-description-main">' + (wsT('chat.agentModeSupervisorHint') || '专家路由场景:监督者通过 transfer 动态分派多个专业子代理') + '</div></div><div class="role-selection-checkmark-main agent-mode-check" data-agent-mode-check="supervisor">\u2713</div></button>' +
'<button type="button" class="role-selection-item-main agent-mode-option ws-agent-mode-option" data-value="eino_single" role="option" onclick="wsSelectAgentMode(\'eino_single\')" data-agent-mode-detail="' + escapeHtmlAttr(wsT('chat.agentModeEinoSingleHint') || 'Eino ChatModelAgent + Runner') + '"><div class="role-selection-item-icon-main">\u26a1</div><div class="role-selection-item-content-main"><div class="role-selection-item-name-main">' + (wsT('chat.agentModeEinoSingle') || 'Eino 单代理(ADK') + '</div><div class="role-selection-item-description-main">' + (wsT('chat.agentModeEinoSingleHint') || 'Eino ChatModelAgent + Runner') + '</div></div><div class="role-selection-checkmark-main agent-mode-check" data-agent-mode-check="eino_single">\u2713</div></button>' +
'<button type="button" class="role-selection-item-main agent-mode-option ws-agent-mode-option" data-value="deep" role="option" onclick="wsSelectAgentMode(\'deep\')" data-agent-mode-detail="' + escapeHtmlAttr(wsT('chat.agentModeDeepHint') || 'Eino DeepAgent,适合复杂安全测试、多阶段 task 子代理委派与汇总') + '"><div class="role-selection-item-icon-main">\ud83e\udde9</div><div class="role-selection-item-content-main"><div class="role-selection-item-name-main">' + (wsT('chat.agentModeDeep') || 'DeepDeepAgent') + '</div><div class="role-selection-item-description-main">' + (wsT('chat.agentModeDeepHint') || 'Eino DeepAgent,适合复杂安全测试、多阶段 task 子代理委派与汇总') + '</div></div><div class="role-selection-checkmark-main agent-mode-check" data-agent-mode-check="deep">\u2713</div></button>' +
'<button type="button" class="role-selection-item-main agent-mode-option ws-agent-mode-option" data-value="plan_execute" role="option" onclick="wsSelectAgentMode(\'plan_execute\')" data-agent-mode-detail="' + escapeHtmlAttr(wsT('chat.agentModePlanExecuteHint') || '规划 → 执行 → 重规划') + '"><div class="role-selection-item-icon-main">\ud83d\udccb</div><div class="role-selection-item-content-main"><div class="role-selection-item-name-main">' + (wsT('chat.agentModePlanExecuteLabel') || 'Plan-Execute') + '</div><div class="role-selection-item-description-main">' + (wsT('chat.agentModePlanExecuteHint') || '规划 → 执行 → 重规划') + '</div></div><div class="role-selection-checkmark-main agent-mode-check" data-agent-mode-check="plan_execute">\u2713</div></button>' +
'<button type="button" class="role-selection-item-main agent-mode-option ws-agent-mode-option" data-value="supervisor" role="option" onclick="wsSelectAgentMode(\'supervisor\')" data-agent-mode-detail="' + escapeHtmlAttr(wsT('chat.agentModeSupervisorHint') || '专家路由场景:监督者通过 transfer 动态分派多个专业子代理') + '"><div class="role-selection-item-icon-main">\ud83c\udfaf</div><div class="role-selection-item-content-main"><div class="role-selection-item-name-main">' + (wsT('chat.agentModeSupervisorLabel') || 'Supervisor(专家路由)') + '</div><div class="role-selection-item-description-main">' + (wsT('chat.agentModeSupervisorHint') || '专家路由场景:监督者通过 transfer 动态分派多个专业子代理') + '</div></div><div class="role-selection-checkmark-main agent-mode-check" data-agent-mode-check="supervisor">\u2713</div></button>' +
'</div></div></div>' +
'<input type="hidden" id="ws-agent-mode-select" value="eino_single" autocomplete="off" />' +
'</div>' +