mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-05-15 04:51:01 +02:00
Add files via upload
This commit is contained in:
+79
-24
@@ -3708,18 +3708,14 @@ header {
|
||||
|
||||
.timeline-item-iteration {
|
||||
border-left-color: var(--accent-color);
|
||||
background: rgba(0, 102, 255, 0.05);
|
||||
background: rgba(0, 102, 255, 0.06);
|
||||
}
|
||||
|
||||
/* Eino 多代理:主编排器 vs 子代理时间线区分 */
|
||||
.timeline-eino-role-orchestrator {
|
||||
border-left-color: #5c6bc0 !important;
|
||||
background: rgba(92, 107, 192, 0.09) !important;
|
||||
}
|
||||
.timeline-eino-role-sub {
|
||||
border-left-color: #00897b !important;
|
||||
background: rgba(0, 137, 123, 0.08) !important;
|
||||
}
|
||||
/*
|
||||
* Eino 主/子代理:保留 timeline-eino-role-* class(由 applyEinoTimelineRole 写入),
|
||||
* 但不再在此处整卡铺色 + !important,否则会盖住工具调用/结果/思考的类型色。
|
||||
* 主编排 vs 子代理的区分由「迭代轮次」上的 timeline-eino-scope-* 负责。
|
||||
*/
|
||||
.timeline-item-iteration.timeline-eino-scope-main {
|
||||
border-left-color: #3949ab !important;
|
||||
background: rgba(57, 73, 171, 0.1) !important;
|
||||
@@ -3729,29 +3725,72 @@ header {
|
||||
background: rgba(0, 105, 92, 0.09) !important;
|
||||
}
|
||||
|
||||
/* 模型内部思考:弱化灰紫,避免与「助手输出」抢视觉 */
|
||||
.timeline-item-thinking {
|
||||
border-left-color: #9c27b0;
|
||||
background: rgba(156, 39, 176, 0.05);
|
||||
border-left-color: #7e57c2;
|
||||
background: rgba(103, 58, 183, 0.06);
|
||||
}
|
||||
|
||||
/* 迭代中主通道流式正文(标题常为「助手输出」等):中性底 + 主色条,表示对用户可见的答复流 */
|
||||
.timeline-item-thinking[data-response-stream-placeholder="1"] {
|
||||
border-left-color: var(--accent-color);
|
||||
background: rgba(0, 102, 255, 0.04);
|
||||
}
|
||||
|
||||
.timeline-item-reasoning_chain {
|
||||
border-left-color: #5c6bc0;
|
||||
background: rgba(92, 107, 192, 0.06);
|
||||
border-left-color: #5e35b1;
|
||||
background: rgba(94, 53, 177, 0.07);
|
||||
}
|
||||
|
||||
.timeline-item-planning {
|
||||
border-left-color: #00838f;
|
||||
background: rgba(0, 131, 143, 0.06);
|
||||
}
|
||||
|
||||
/* 工具调用:信息色(蓝),与「结果绿/红」分离;完成态不再用绿色以免与成功结果混淆 */
|
||||
.timeline-item-tool_call {
|
||||
border-left-color: #ff9800;
|
||||
background: rgba(255, 152, 0, 0.05);
|
||||
border-left-color: #1565c0;
|
||||
background: rgba(21, 101, 192, 0.07);
|
||||
}
|
||||
|
||||
.timeline-item-tool_result {
|
||||
border-left-color: #78909c;
|
||||
background: rgba(120, 144, 156, 0.06);
|
||||
}
|
||||
|
||||
.timeline-item-tool_result:has(.tool-result-section.success) {
|
||||
border-left-color: var(--success-color);
|
||||
background: rgba(40, 167, 69, 0.05);
|
||||
background: rgba(40, 167, 69, 0.07);
|
||||
}
|
||||
|
||||
.timeline-item-tool_result:has(.tool-result-section.error) {
|
||||
border-left-color: var(--error-color);
|
||||
background: rgba(220, 53, 69, 0.07);
|
||||
}
|
||||
|
||||
.timeline-item-tool_result.error {
|
||||
border-left-color: var(--error-color);
|
||||
background: rgba(220, 53, 69, 0.05);
|
||||
background: rgba(220, 53, 69, 0.07);
|
||||
}
|
||||
|
||||
.timeline-item-eino_agent_reply {
|
||||
border-left-color: #6a1b9a;
|
||||
background: rgba(106, 27, 154, 0.07);
|
||||
}
|
||||
|
||||
.timeline-item-progress {
|
||||
border-left-color: #607d8b;
|
||||
background: rgba(96, 125, 139, 0.08);
|
||||
}
|
||||
|
||||
.timeline-item-warning {
|
||||
border-left-color: #f57c00;
|
||||
background: rgba(245, 124, 0, 0.09);
|
||||
}
|
||||
|
||||
.timeline-item-tool_calls_detected {
|
||||
border-left-color: #0277bd;
|
||||
background: rgba(2, 119, 189, 0.06);
|
||||
}
|
||||
|
||||
.timeline-item-error {
|
||||
@@ -3941,20 +3980,36 @@ header {
|
||||
border: 1px solid rgba(220, 53, 69, 0.3);
|
||||
}
|
||||
|
||||
/* 工具调用项状态样式 */
|
||||
/* 工具调用项状态:全程保持「信息蓝」系,完成态不用绿色(避免与工具成功结果混淆) */
|
||||
.timeline-item-tool_call.tool-call-running {
|
||||
border-left-color: var(--accent-color);
|
||||
background: rgba(0, 102, 255, 0.08);
|
||||
border-left-color: #42a5f5;
|
||||
background: rgba(66, 165, 245, 0.1);
|
||||
}
|
||||
|
||||
.timeline-item-tool_call.tool-call-completed {
|
||||
border-left-color: var(--success-color);
|
||||
background: rgba(40, 167, 69, 0.08);
|
||||
border-left-color: #0d47a1;
|
||||
background: rgba(13, 71, 161, 0.08);
|
||||
}
|
||||
|
||||
.timeline-item-tool_call.tool-call-failed {
|
||||
border-left-color: var(--error-color);
|
||||
background: rgba(220, 53, 69, 0.08);
|
||||
background: rgba(220, 53, 69, 0.1);
|
||||
}
|
||||
|
||||
/* 参数块与卡片类型色弱对齐,扫读时一眼归到「调用」 */
|
||||
.timeline-item-tool_call .tool-args {
|
||||
background: rgba(21, 101, 192, 0.06);
|
||||
border-color: rgba(21, 101, 192, 0.22);
|
||||
}
|
||||
|
||||
.timeline-item-tool_result:has(.tool-result-section.success) .tool-result {
|
||||
background: rgba(40, 167, 69, 0.08);
|
||||
border-color: rgba(40, 167, 69, 0.35);
|
||||
}
|
||||
|
||||
.timeline-item-tool_result:has(.tool-result-section.error) .tool-result {
|
||||
background: rgba(220, 53, 69, 0.1);
|
||||
border-color: rgba(220, 53, 69, 0.45);
|
||||
}
|
||||
|
||||
/* 活跃任务栏 */
|
||||
|
||||
+13
-8
@@ -342,22 +342,27 @@ function formatMarkdown(text) {
|
||||
ALLOWED_ATTR: ['href', 'title', 'alt', 'src', 'class'],
|
||||
ALLOW_DATA_ATTR: false,
|
||||
};
|
||||
|
||||
|
||||
const raw = text == null ? '' : String(text);
|
||||
const src = typeof window.normalizeAssistantMarkdownSource === 'function'
|
||||
? window.normalizeAssistantMarkdownSource(raw)
|
||||
: raw;
|
||||
|
||||
if (typeof DOMPurify !== 'undefined') {
|
||||
if (typeof marked !== 'undefined' && !/<[a-z][\s\S]*>/i.test(text)) {
|
||||
if (typeof marked !== 'undefined' && !/<[a-z][\s\S]*>/i.test(src)) {
|
||||
try {
|
||||
marked.setOptions({
|
||||
breaks: true,
|
||||
gfm: true,
|
||||
});
|
||||
let parsedContent = marked.parse(text);
|
||||
const parsedContent = marked.parse(src, { async: false });
|
||||
return DOMPurify.sanitize(parsedContent, sanitizeConfig);
|
||||
} catch (e) {
|
||||
console.error('Markdown 解析失败:', e);
|
||||
return DOMPurify.sanitize(text, sanitizeConfig);
|
||||
return DOMPurify.sanitize(src, sanitizeConfig);
|
||||
}
|
||||
} else {
|
||||
return DOMPurify.sanitize(text, sanitizeConfig);
|
||||
return DOMPurify.sanitize(src, sanitizeConfig);
|
||||
}
|
||||
} else if (typeof marked !== 'undefined') {
|
||||
try {
|
||||
@@ -365,13 +370,13 @@ function formatMarkdown(text) {
|
||||
breaks: true,
|
||||
gfm: true,
|
||||
});
|
||||
return marked.parse(text);
|
||||
return marked.parse(src, { async: false });
|
||||
} catch (e) {
|
||||
console.error('Markdown 解析失败:', e);
|
||||
return escapeHtml(text).replace(/\n/g, '<br>');
|
||||
return escapeHtml(src).replace(/\n/g, '<br>');
|
||||
}
|
||||
} else {
|
||||
return escapeHtml(text).replace(/\n/g, '<br>');
|
||||
return escapeHtml(src).replace(/\n/g, '<br>');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1844,7 +1844,10 @@ function refreshSystemReadyMessageBubbles() {
|
||||
if (typeof marked !== 'undefined') {
|
||||
try {
|
||||
marked.setOptions({ breaks: true, gfm: true });
|
||||
const parsed = marked.parse(text);
|
||||
const src = typeof window.normalizeAssistantMarkdownSource === 'function'
|
||||
? window.normalizeAssistantMarkdownSource(text)
|
||||
: text;
|
||||
const parsed = marked.parse(src, { async: false });
|
||||
formattedContent = typeof DOMPurify !== 'undefined'
|
||||
? DOMPurify.sanitize(parsed, defaultSanitizeConfig)
|
||||
: parsed;
|
||||
@@ -1935,7 +1938,10 @@ function addMessage(role, content, mcpExecutionIds = null, progressId = null, cr
|
||||
breaks: true,
|
||||
gfm: true,
|
||||
});
|
||||
return marked.parse(raw);
|
||||
const src = typeof window.normalizeAssistantMarkdownSource === 'function'
|
||||
? window.normalizeAssistantMarkdownSource(raw)
|
||||
: raw;
|
||||
return marked.parse(src, { async: false });
|
||||
} catch (e) {
|
||||
console.error('Markdown 解析失败:', e);
|
||||
return null;
|
||||
|
||||
+112
-1
@@ -273,6 +273,116 @@ function escapeHtmlLocal(text) {
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
/** fenced 块占位(BMP 私用区,正文几乎不会出现) */
|
||||
const _MD_FENCE_PRE = '\n\uE000CSAI_FENCE_';
|
||||
const _MD_FENCE_SUF = '_\uE000\n';
|
||||
|
||||
function _maskFencedCodeBlocksForMdPreprocess(md) {
|
||||
const blocks = [];
|
||||
const masked = String(md).replace(/```[\s\S]*?```/g, (m) => {
|
||||
const i = blocks.length;
|
||||
blocks.push(m);
|
||||
return _MD_FENCE_PRE + i + _MD_FENCE_SUF;
|
||||
});
|
||||
return { masked, blocks };
|
||||
}
|
||||
|
||||
function _unmaskFencedCodeBlocksAfterMdPreprocess(s, blocks) {
|
||||
let out = s;
|
||||
for (let i = 0; i < blocks.length; i++) {
|
||||
out = out.split(_MD_FENCE_PRE + i + _MD_FENCE_SUF).join(blocks[i]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模型/网关偶发把「思考」混进正文,用伪 XML 包裹(如 <redacted_thinking>…</redacted_thinking>)。
|
||||
* 与 Markdown 列表混排时,结束标签常被吞进 <li>,其后 **、` 等行内语法全部无法解析;成对块整段移除。
|
||||
* @param {string} segment
|
||||
* @returns {string}
|
||||
*/
|
||||
function _stripXmlReasoningWrappersForMarkdown(segment) {
|
||||
let t = String(segment);
|
||||
const tags = ['redacted_thinking', 'redacted_reasoning'];
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
const name = tags[i];
|
||||
const re = new RegExp('<\\s*' + name + '\\b[^>]*>[\\s\\S]*?<\\s*/\\s*' + name + '\\s*>', 'gi');
|
||||
t = t.replace(re, '\n\n');
|
||||
}
|
||||
return t.replace(/\n{3,}/g, '\n\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除 LLM 常用的块级 HTML 外壳(`<div>`、`<p>`、`<section>`、`<article>`、`<main>`)。
|
||||
* 整段包在块级标签里时,CommonMark 不会在块内再解析 Markdown,导致 **、` 原样显示。
|
||||
*/
|
||||
function _unwrapHtmlBlockWrappersForMarkdown(segment) {
|
||||
let s = segment;
|
||||
let prev;
|
||||
for (let i = 0; i < 30 && s !== prev; i++) {
|
||||
prev = s;
|
||||
s = s.replace(/<div(?:\s[^>]*)?>([\s\S]*?)<\/div>/gi, (_, inner) => String(inner).trim() + '\n\n');
|
||||
s = s.replace(/<p(?:\s[^>]*)?>([\s\S]*?)<\/p>/gi, (_, inner) => String(inner).trim() + '\n\n');
|
||||
s = s.replace(/<section(?:\s[^>]*)?>([\s\S]*?)<\/section>/gi, (_, inner) => String(inner).trim() + '\n\n');
|
||||
s = s.replace(/<article(?:\s[^>]*)?>([\s\S]*?)<\/article>/gi, (_, inner) => String(inner).trim() + '\n\n');
|
||||
s = s.replace(/<main(?:\s[^>]*)?>([\s\S]*?)<\/main>/gi, (_, inner) => String(inner).trim() + '\n\n');
|
||||
s = s.replace(/\n{3,}/g, '\n\n');
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 HTML 列表 / 粘连的 `<li>` 还原为 Markdown 列表行,并去掉外层 `<ul>`,便于 marked 解析行内 **、` `
|
||||
* @param {string} segment
|
||||
* @returns {string}
|
||||
*/
|
||||
function _flattenOrphanHtmlLiInMarkdown(segment) {
|
||||
let s = segment;
|
||||
s = s.replace(/<li(?:\s[^>]*)?>([\s\S]*?)<\/li>/gi, (_, inner) => {
|
||||
const body = String(inner).trim().replace(/\s*\n\s*/g, ' ');
|
||||
return '- ' + body + '\n';
|
||||
});
|
||||
s = s.replace(/<\/?ul(?:\s[^>]*)?>/gi, '\n');
|
||||
s = s.replace(/<\/?ol(?:\s[^>]*)?>/gi, '\n');
|
||||
s = s.replace(/([0-9A-Za-z_\u4e00-\u9fff])\s*<li(?:\s[^>]*)?>\s*/g, (_, ch) => ch + '\n- ');
|
||||
return s.replace(/\n{3,}/g, '\n\n');
|
||||
}
|
||||
|
||||
/** 行首 Unicode 项目符号 → Markdown 列表 `- `(模型常用 • 而非 `-`) */
|
||||
function _normalizeUnicodeBulletMarkersToMdDash(segment) {
|
||||
return segment
|
||||
.replace(/^\s*\u2022\s+/gm, '- ')
|
||||
.replace(/^\s*\u00b7\s+/gm, '- ');
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析前归一化助手 Markdown:去掉零宽字符,NFKC 将全角 * ` _ 等转为 ASCII,
|
||||
* 避免 marked 无法识别强调/行内代码而原样显示 **、反引号;
|
||||
* 并移除 <redacted_thinking> 等伪 XML 思考块、修正块级 HTML(`<div>`/`<p>`/…、`<ul>`/`<li>`)与 Unicode 项目符号 `•`,避免块级 HTML 吞掉 inline 解析。
|
||||
* @param {string|null|undefined} text
|
||||
* @returns {string}
|
||||
*/
|
||||
function normalizeAssistantMarkdownSource(text) {
|
||||
if (text == null) return '';
|
||||
let s = String(text);
|
||||
s = s.replace(/[\u200B-\u200D\u200E\u200F\uFEFF\u2060]/g, '');
|
||||
try {
|
||||
s = s.normalize('NFKC');
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
s = _stripXmlReasoningWrappersForMarkdown(s);
|
||||
const fb = _maskFencedCodeBlocksForMdPreprocess(s);
|
||||
s = _unwrapHtmlBlockWrappersForMarkdown(fb.masked);
|
||||
s = _flattenOrphanHtmlLiInMarkdown(s);
|
||||
s = _normalizeUnicodeBulletMarkersToMdDash(s);
|
||||
s = _unmaskFencedCodeBlocksAfterMdPreprocess(s, fb.blocks);
|
||||
return s;
|
||||
}
|
||||
if (typeof window !== 'undefined') {
|
||||
window.normalizeAssistantMarkdownSource = normalizeAssistantMarkdownSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与 internal/openai.normalizeStreamingDelta 一致:兼容网关/模型返回「累计全文」或整包重发,
|
||||
* 避免前端 buffer += chunk 与后端已归一化的增量叠加导致逐段重复(如「响应中显示了响应中显示了」)。
|
||||
@@ -316,10 +426,11 @@ function setTimelineItemContentStreamRich(contentEl, html) {
|
||||
|
||||
function formatAssistantMarkdownContent(text) {
|
||||
const raw = text == null ? '' : String(text);
|
||||
const src = normalizeAssistantMarkdownSource(raw);
|
||||
if (typeof marked !== 'undefined') {
|
||||
try {
|
||||
marked.setOptions({ breaks: true, gfm: true });
|
||||
const parsed = marked.parse(raw);
|
||||
const parsed = marked.parse(src, { async: false });
|
||||
if (typeof DOMPurify !== 'undefined') {
|
||||
return DOMPurify.sanitize(parsed, assistantMarkdownSanitizeConfig);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user