feat(web): improve Mermaid diagram styling
This commit is contained in:
parent
a1068f0ec1
commit
aa8ccec738
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- Mermaid 图表采用更清晰的科研配色与更精致的图框,新生成的流程图还会按数据、处理、判断、结果等角色使用协调的语义色,减少单调的灰白图。
|
||||||
|
|
||||||
- 管理后台总览按“当前运行”和“运营资源”重新组织,执行容量改为精简摘要并可从右侧详情面板查看队列、容器、用户占用与宿主资源,异常状态和近期指标也更容易识别。
|
- 管理后台总览按“当前运行”和“运营资源”重新组织,执行容量改为精简摘要并可从右侧详情面板查看队列、容器、用户占用与宿主资源,异常状态和近期指标也更容易识别。
|
||||||
|
|
||||||
## 0.70.0 — 2026-09-02
|
## 0.70.0 — 2026-09-02
|
||||||
|
|
@ -20,8 +22,6 @@
|
||||||
|
|
||||||
- 专业软件节点启用完整的新管理界面,节点状态、首次注册、软件环境、本机任务、ANSYS 验收和数据目录迁移均可直接管理;应用图标、导航、状态提示和窄窗口布局也更清晰,关闭窗口后仍会继续在托盘运行。
|
- 专业软件节点启用完整的新管理界面,节点状态、首次注册、软件环境、本机任务、ANSYS 验收和数据目录迁移均可直接管理;应用图标、导航、状态提示和窄窗口布局也更清晰,关闭窗口后仍会继续在托盘运行。
|
||||||
|
|
||||||
- 删除已发布文件后会释放个人存储配额;管理员可分别查看用户当前文件、隐藏回收站及物理总占用。
|
|
||||||
|
|
||||||
## 0.69.1 — 2026-08-27
|
## 0.69.1 — 2026-08-27
|
||||||
|
|
||||||
- 专业软件任务的进度与结果改为在当前对话左侧集中堆叠,不再用多条结果消息打断正文;可直接打开 Blender 等软件的多张预览,手机端从“软件结果”入口统一查看。
|
- 专业软件任务的进度与结果改为在当前对话左侧集中堆叠,不再用多条结果消息打断正文;可直接打开 Blender 等软件的多张预览,手机端从“软件结果”入口统一查看。
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,14 @@ _HOST_ENV_BLOCK = """\
|
||||||
## 运行环境(本地 host)
|
## 运行环境(本地 host)
|
||||||
`shell` 走的是 **Windows cmd.exe**(非 bash):避免 unix-only flag,`mkdir -p` 不识别 → 用 `run_python` 的 `os.makedirs(..., exist_ok=True)` 建目录;复杂管道/重定向用 run_python 更稳。"""
|
`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:
|
def load_config() -> dict:
|
||||||
return yaml.safe_load((ROOT / "config" / "agent.yaml").read_text(encoding="utf-8")) or {}
|
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"
|
is_docker = os.getenv("ZCBOT_SANDBOX_BACKEND", "host").lower() == "docker"
|
||||||
# 运行环境段紧跟模板(平台/网络是基础事实,放前面);general_v1 的「平台」段指向这里。
|
# 运行环境段紧跟模板(平台/网络是基础事实,放前面);general_v1 的「平台」段指向这里。
|
||||||
prompt += _CONTAINER_ENV_BLOCK if is_docker else _HOST_ENV_BLOCK
|
prompt += _CONTAINER_ENV_BLOCK if is_docker else _HOST_ENV_BLOCK
|
||||||
|
prompt += _MERMAID_STYLE_BLOCK
|
||||||
if skills.skills:
|
if skills.skills:
|
||||||
prompt += f"\n\n## 可用 skill (用 load_skill 加载完整指引)\n{skills.discovery_block()}"
|
prompt += f"\n\n## 可用 skill (用 load_skill 加载完整指引)\n{skills.discovery_block()}"
|
||||||
# .memory/ 在 agent 视角下的可写路径:docker 给容器路径,host 给宿主绝对路径。
|
# .memory/ 在 agent 视角下的可写路径:docker 给容器路径,host 给宿主绝对路径。
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,11 @@ test("renders completed mermaid blocks with strict local configuration", async (
|
||||||
assert.equal(calls.initialize.length, 1);
|
assert.equal(calls.initialize.length, 1);
|
||||||
assert.equal(calls.initialize[0].securityLevel, "strict");
|
assert.equal(calls.initialize[0].securityLevel, "strict");
|
||||||
assert.equal(calls.initialize[0].startOnLoad, false);
|
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(calls.render[0].source, "flowchart LR\nA --> B");
|
||||||
assert.equal(fixture.pre.replacement.className, "mermaid-diagram");
|
assert.equal(fixture.pre.replacement.className, "mermaid-diagram");
|
||||||
assert.equal(fixture.pre.replacement.children[0].innerHTML, "<svg viewBox='0 0 10 10'></svg>");
|
assert.equal(fixture.pre.replacement.children[0].innerHTML, "<svg viewBox='0 0 10 10'></svg>");
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,15 @@ class TestSystemPromptPaths(unittest.TestCase):
|
||||||
self.assertIn(str(wd), prompt)
|
self.assertIn(str(wd), prompt)
|
||||||
self.assertNotIn("/workspace/数据资源展示", 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):
|
def test_rename_guard_only_when_tool_is_available(self):
|
||||||
interactive, _, _ = _build("docker", allow_rename=True)
|
interactive, _, _ = _build("docker", allow_rename=True)
|
||||||
scheduled, _, _ = _build("docker", allow_rename=False)
|
scheduled, _, _ = _build("docker", allow_rename=False)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,53 @@ const MERMAID_CONFIG = Object.freeze({
|
||||||
startOnLoad: false,
|
startOnLoad: false,
|
||||||
securityLevel: "strict",
|
securityLevel: "strict",
|
||||||
suppressErrorRendering: true,
|
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,
|
maxTextSize: 50000,
|
||||||
maxEdges: 500,
|
maxEdges: 500,
|
||||||
fontFamily: '-apple-system, "Segoe UI", "Microsoft YaHei", sans-serif',
|
fontFamily: '-apple-system, "Segoe UI", "Microsoft YaHei", sans-serif',
|
||||||
|
|
|
||||||
|
|
@ -442,11 +442,12 @@ header .who {
|
||||||
|
|
||||||
.mermaid-diagram {
|
.mermaid-diagram {
|
||||||
margin: 14px 0;
|
margin: 14px 0;
|
||||||
padding: 14px;
|
padding: 18px;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
border: 1px solid var(--border-soft);
|
border: 1px solid #dce6f2;
|
||||||
border-radius: var(--r-lg);
|
border-radius: var(--r-xl);
|
||||||
background: #ffffff;
|
background: linear-gradient(145deg, #ffffff 0%, #f8fbff 100%);
|
||||||
|
box-shadow: 0 6px 22px rgba(50, 79, 108, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mermaid-viewport {
|
.mermaid-viewport {
|
||||||
|
|
@ -458,6 +459,7 @@ header .who {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
filter: drop-shadow(0 2px 3px rgba(38, 61, 82, 0.06));
|
||||||
}
|
}
|
||||||
|
|
||||||
.mermaid-notice {
|
.mermaid-notice {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue