diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f1c68f..df9d25b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ## Unreleased +- Mermaid 图表采用更清晰的科研配色与更精致的图框,新生成的流程图还会按数据、处理、判断、结果等角色使用协调的语义色,减少单调的灰白图。 + - 管理后台总览按“当前运行”和“运营资源”重新组织,执行容量改为精简摘要并可从右侧详情面板查看队列、容器、用户占用与宿主资源,异常状态和近期指标也更容易识别。 ## 0.70.0 — 2026-09-02 @@ -20,8 +22,6 @@ - 专业软件节点启用完整的新管理界面,节点状态、首次注册、软件环境、本机任务、ANSYS 验收和数据目录迁移均可直接管理;应用图标、导航、状态提示和窄窗口布局也更清晰,关闭窗口后仍会继续在托盘运行。 -- 删除已发布文件后会释放个人存储配额;管理员可分别查看用户当前文件、隐藏回收站及物理总占用。 - ## 0.69.1 — 2026-08-27 - 专业软件任务的进度与结果改为在当前对话左侧集中堆叠,不再用多条结果消息打断正文;可直接打开 Blender 等软件的多张预览,手机端从“软件结果”入口统一查看。 diff --git a/core/agent_builder.py b/core/agent_builder.py index 078dbda..1ecbec6 100644 --- a/core/agent_builder.py +++ b/core/agent_builder.py @@ -161,6 +161,14 @@ _HOST_ENV_BLOCK = """\ ## 运行环境(本地 host) `shell` 走的是 **Windows cmd.exe**(非 bash):避免 unix-only flag,`mkdir -p` 不识别 → 用 `run_python` 的 `os.makedirs(..., exist_ok=True)` 建目录;复杂管道/重定向用 run_python 更稳。""" +_MERMAID_STYLE_BLOCK = """\ + +## Mermaid 视觉规范 +生成 Mermaid 图时默认采用**克制的语义配色**,不要输出全灰单色图,也不要随机彩虹色: +- 流程图 / 架构图用 `classDef` 按角色分组,通常 3–5 色足够:输入/数据用蓝色,处理/模块用青绿色,判断/风险用橙色,结果/成功用绿色,异常用红色;同类节点保持同色。 +- 使用浅色填充、较深描边与高对比深色文字,线条颜色统一;分组框比节点更淡,避免大面积高饱和背景。 +- 颜色服务于信息层级;若用户指定品牌色、黑白打印或已有 `classDef` / `style` 规则,优先遵循用户设置,不要覆盖。""" + def load_config() -> dict: return yaml.safe_load((ROOT / "config" / "agent.yaml").read_text(encoding="utf-8")) or {} @@ -363,6 +371,7 @@ def _build_system_prompt( is_docker = os.getenv("ZCBOT_SANDBOX_BACKEND", "host").lower() == "docker" # 运行环境段紧跟模板(平台/网络是基础事实,放前面);general_v1 的「平台」段指向这里。 prompt += _CONTAINER_ENV_BLOCK if is_docker else _HOST_ENV_BLOCK + prompt += _MERMAID_STYLE_BLOCK if skills.skills: prompt += f"\n\n## 可用 skill (用 load_skill 加载完整指引)\n{skills.discovery_block()}" # .memory/ 在 agent 视角下的可写路径:docker 给容器路径,host 给宿主绝对路径。 diff --git a/tests/frontend_markdown_guard.test.mjs b/tests/frontend_markdown_guard.test.mjs index d7306f2..aa33c7f 100644 --- a/tests/frontend_markdown_guard.test.mjs +++ b/tests/frontend_markdown_guard.test.mjs @@ -78,6 +78,11 @@ test("renders completed mermaid blocks with strict local configuration", async ( assert.equal(calls.initialize.length, 1); assert.equal(calls.initialize[0].securityLevel, "strict"); assert.equal(calls.initialize[0].startOnLoad, false); + assert.equal(calls.initialize[0].theme, "base"); + assert.equal(calls.initialize[0].themeVariables.primaryColor, "#eaf2ff"); + assert.equal(calls.initialize[0].themeVariables.secondaryColor, "#e8f7f3"); + assert.equal(calls.initialize[0].themeVariables.tertiaryColor, "#fff4df"); + assert.equal(calls.initialize[0].themeVariables.pie5, "#d96c75"); assert.equal(calls.render[0].source, "flowchart LR\nA --> B"); assert.equal(fixture.pre.replacement.className, "mermaid-diagram"); assert.equal(fixture.pre.replacement.children[0].innerHTML, ""); diff --git a/tests/test_system_prompt_paths.py b/tests/test_system_prompt_paths.py index 8ce8b81..5cb28e1 100644 --- a/tests/test_system_prompt_paths.py +++ b/tests/test_system_prompt_paths.py @@ -75,6 +75,15 @@ class TestSystemPromptPaths(unittest.TestCase): self.assertIn(str(wd), prompt) self.assertNotIn("/workspace/数据资源展示", prompt) + def test_mermaid_visual_guidance_is_backend_independent(self): + for backend in ("docker", "host"): + with self.subTest(backend=backend): + prompt, _, _ = _build(backend) + self.assertIn("## Mermaid 视觉规范", prompt) + self.assertIn("`classDef` 按角色分组", prompt) + self.assertIn("不要输出全灰单色图", prompt) + self.assertIn("用户指定品牌色", prompt) + def test_rename_guard_only_when_tool_is_available(self): interactive, _, _ = _build("docker", allow_rename=True) scheduled, _, _ = _build("docker", allow_rename=False) diff --git a/web/static/js/markdown.js b/web/static/js/markdown.js index c79096e..f923eb5 100644 --- a/web/static/js/markdown.js +++ b/web/static/js/markdown.js @@ -11,7 +11,53 @@ const MERMAID_CONFIG = Object.freeze({ startOnLoad: false, securityLevel: "strict", suppressErrorRendering: true, - theme: "neutral", + // A restrained scientific palette. Author-defined classDef/style rules still + // take precedence, while unstyled diagrams get a consistent visual baseline. + theme: "base", + themeVariables: Object.freeze({ + background: "#ffffff", + primaryColor: "#eaf2ff", + primaryTextColor: "#17324d", + primaryBorderColor: "#5b8fc9", + secondaryColor: "#e8f7f3", + secondaryTextColor: "#163c35", + secondaryBorderColor: "#4f9f8d", + tertiaryColor: "#fff4df", + tertiaryTextColor: "#594018", + tertiaryBorderColor: "#d79a3b", + lineColor: "#60758a", + textColor: "#25384a", + mainBkg: "#eaf2ff", + secondBkg: "#e8f7f3", + border1: "#5b8fc9", + border2: "#4f9f8d", + arrowheadColor: "#60758a", + clusterBkg: "#f7f9fc", + clusterBorder: "#b9c8d8", + actorBkg: "#eaf2ff", + actorBorder: "#5b8fc9", + actorTextColor: "#17324d", + actorLineColor: "#8ca0b3", + signalColor: "#536b82", + signalTextColor: "#25384a", + labelBoxBkgColor: "#f7f9fc", + labelBoxBorderColor: "#b9c8d8", + labelTextColor: "#25384a", + loopTextColor: "#25384a", + noteBkgColor: "#fff4df", + noteBorderColor: "#d79a3b", + noteTextColor: "#594018", + activationBkgColor: "#d9eee9", + activationBorderColor: "#4f9f8d", + pie1: "#4f7cac", + pie2: "#5ca89a", + pie3: "#e4a64a", + pie4: "#a879b2", + pie5: "#d96c75", + pie6: "#6d9dc5", + pie7: "#84b59f", + pie8: "#d8b365", + }), maxTextSize: 50000, maxEdges: 500, fontFamily: '-apple-system, "Segoe UI", "Microsoft YaHei", sans-serif', diff --git a/web/static/ui.css b/web/static/ui.css index ebbb8aa..1b53478 100644 --- a/web/static/ui.css +++ b/web/static/ui.css @@ -442,11 +442,12 @@ header .who { .mermaid-diagram { margin: 14px 0; - padding: 14px; + padding: 18px; overflow-x: auto; - border: 1px solid var(--border-soft); - border-radius: var(--r-lg); - background: #ffffff; + border: 1px solid #dce6f2; + border-radius: var(--r-xl); + background: linear-gradient(145deg, #ffffff 0%, #f8fbff 100%); + box-shadow: 0 6px 22px rgba(50, 79, 108, 0.08); } .mermaid-viewport { @@ -458,6 +459,7 @@ header .who { max-width: 100%; height: auto; margin: 0 auto; + filter: drop-shadow(0 2px 3px rgba(38, 61, 82, 0.06)); } .mermaid-notice {