minimal-agent 0.18.5 → 0.18.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/plugins/workflow-runner/src/runtime/context.js +5 -17
- package/plugins/workflow-runner/src/runtime/context.ts +4 -15
- package/plugins/workflow-runner/src/runtime/model.js +4 -2
- package/plugins/workflow-runner/src/runtime/model.ts +3 -2
- package/plugins/workflow-runner/test/api.test.ts +4 -2
- package/plugins/workflow-runner/test/embeddedExecution.test.ts +5 -6
- package/plugins/workflow-runner/test/modelOutcome.test.ts +36 -0
- package/skills/dynamic-ui/SKILL.md +38 -278
- package/skills/dynamic-ui/mermaid.md +32 -0
- package/skills/dynamic-ui/scenes/architecture-and-flow.md +4 -1
- package/skills/dynamic-ui/scenes/data-visualization.md +5 -2
- package/skills/dynamic-ui/svg-design.md +251 -0
- package/skills/dynamic-ui/templates/README.md +2 -1
- package/skills/dynamic-ui/tokens/visual-tokens.md +4 -5
- package/src/advisor/README.md +24 -0
- package/src/advisor/advisorCoordinator.js +101 -45
- package/src/advisor/advisorCoordinator.ts +90 -51
- package/src/advisor/deliveryReview.js +35 -0
- package/src/advisor/deliveryReview.ts +30 -0
- package/src/advisor/presentation.js +10 -0
- package/src/advisor/presentation.ts +12 -0
- package/src/advisor/reviewContext.js +60 -0
- package/src/advisor/reviewContext.ts +72 -0
- package/src/context/contextBundle.js +23 -3
- package/src/context/contextBundle.ts +28 -7
- package/src/context/tokenCounter.js +3 -0
- package/src/context/tokenCounter.ts +2 -0
- package/src/llm/messageProvenance.js +1 -1
- package/src/llm/messageProvenance.ts +1 -1
- package/src/loop.js +203 -30
- package/src/loop.ts +183 -30
- package/src/memory/experienceStore.js +1 -0
- package/src/memory/experienceStore.ts +1 -0
- package/src/memory/memoryService.js +3 -2
- package/src/memory/memoryService.ts +3 -0
- package/src/memory/projectKnowledge.js +1 -0
- package/src/memory/projectKnowledge.ts +1 -0
- package/src/memory/retrieval.js +4 -4
- package/src/memory/retrieval.ts +4 -4
- package/src/memory/types.ts +1 -0
- package/src/prompts/policyRegistry.js +31 -17
- package/src/prompts/policyRegistry.ts +31 -16
- package/src/prompts/promptAssembly.js +3 -9
- package/src/prompts/promptAssembly.ts +3 -9
- package/src/runtime/answerPreparation.js +9 -0
- package/src/runtime/answerPreparation.ts +11 -0
- package/src/runtime/eventSemantics.js +2 -0
- package/src/runtime/eventSemantics.ts +2 -0
- package/src/runtime/finalAnswer.js +7 -0
- package/src/runtime/finalAnswer.ts +9 -0
- package/src/runtime/lifecycleTrace.js +16 -0
- package/src/runtime/lifecycleTrace.ts +22 -0
- package/src/session/runTurn.js +15 -1
- package/src/session/runTurn.ts +13 -1
- package/src/tools/advisor/advisor.js +56 -40
- package/src/tools/advisor/advisor.ts +57 -39
- package/src/tools/advisor/advisorDecision.js +6 -3
- package/src/tools/advisor/advisorDecision.ts +6 -3
- package/src/tools/agent/agent.js +7 -1
- package/src/tools/agent/agent.ts +7 -1
- package/src/tools/bash/semantics.js +91 -26
- package/src/tools/bash/semantics.ts +63 -33
- package/src/tools/index.js +4 -1
- package/src/tools/index.ts +4 -1
- package/src/types.ts +15 -6
- package/src/ui/LiveArea.js +2 -1
- package/src/ui/LiveArea.tsx +2 -1
- package/src/ui/MessageList.js +18 -5
- package/src/ui/MessageList.tsx +25 -3
- package/src/ui/draftNotice.js +1 -8
- package/src/ui/draftNotice.ts +1 -10
- package/src/ui/hooks/useChat.js +25 -1
- package/src/ui/hooks/useChat.ts +22 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "minimal-agent",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.7",
|
|
4
4
|
"description": "Bun-first 的本地 Agent 内核,支持工具、插件、工作流、上下文压缩、长期记忆与 OpenAI 兼容接口;Tauri 桌面宿主和 Node.js npm 发布可复用同一内核",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Bill Wang <leiwang0359@gmail.com>",
|
|
@@ -29,7 +29,7 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
29
29
|
import { existsSync } from 'node:fs';
|
|
30
30
|
import { readFile } from 'node:fs/promises';
|
|
31
31
|
import { dirname, isAbsolute, resolve } from 'node:path';
|
|
32
|
-
import { ALL_TOOLS, createAgentRuntimeContext,
|
|
32
|
+
import { ALL_TOOLS, createAgentRuntimeContext, parseCheckString, recordRead, resolveProviderForModel, resolveProviderForWorkflowRole, runQuery, chat as sdkChat, runVerification, executeTool as sdkExecuteTool, stripThinkBlocks, } from '../../../../src/plugin-sdk.js';
|
|
33
33
|
import { withGoalAnchor } from '../goalAnchor.js';
|
|
34
34
|
import { MAIN_LANE } from '../journal.js';
|
|
35
35
|
import { extractJsonPayload } from '../jsonPayload.js';
|
|
@@ -401,8 +401,7 @@ export function createWorkflowContext(deps) {
|
|
|
401
401
|
: opts.allowedTools.filter((n) => hostTools.has(n));
|
|
402
402
|
const toolByName = new Map((ctx.tools ?? []).map((tool) => [tool.name, tool]));
|
|
403
403
|
const allowed = opts.toolMode === 'read-only'
|
|
404
|
-
? requested.filter((name) => name
|
|
405
|
-
name === 'PowerShell' ||
|
|
404
|
+
? requested.filter((name) => name !== 'Bash' && name !== 'PowerShell' &&
|
|
406
405
|
toolByName.get(name)?.isReadOnly === true)
|
|
407
406
|
: requested;
|
|
408
407
|
// ⚠️ 过滤掉的名字必须报出来,而且要**区分两种原因** ——
|
|
@@ -443,7 +442,7 @@ export function createWorkflowContext(deps) {
|
|
|
443
442
|
`本节点实际可用工具只有:${allowed.length ? allowed.join('、') : '无'}。`,
|
|
444
443
|
'宿主系统提示或既往历史中提及的其他工具不属于本节点,不得调用。',
|
|
445
444
|
opts.toolMode === 'read-only'
|
|
446
|
-
? '
|
|
445
|
+
? '本节点严格只读:不得创建、覆盖、修改、移动或删除文件;使用 Read/Glob/Grep 等只读工具,不提供 Bash/PowerShell 通用执行。完成调查后直接返回事实报告。'
|
|
447
446
|
: '',
|
|
448
447
|
].filter(Boolean).join('\n')
|
|
449
448
|
: '';
|
|
@@ -700,20 +699,9 @@ export function createWorkflowContext(deps) {
|
|
|
700
699
|
recovery: 'change-input',
|
|
701
700
|
sensitivity: 'public',
|
|
702
701
|
});
|
|
703
|
-
const readOnlyDenial = (name,
|
|
702
|
+
const readOnlyDenial = (name, _rawArgs) => {
|
|
704
703
|
if (name === 'Bash' || name === 'PowerShell') {
|
|
705
|
-
|
|
706
|
-
try {
|
|
707
|
-
const parsed = JSON.parse(rawArgs);
|
|
708
|
-
if (typeof parsed.command === 'string')
|
|
709
|
-
command = parsed.command;
|
|
710
|
-
}
|
|
711
|
-
catch {
|
|
712
|
-
return readOnlyFailure(name, '命令参数不是合法 JSON');
|
|
713
|
-
}
|
|
714
|
-
return !command || !isReadOnlyShellCommand(command)
|
|
715
|
-
? readOnlyFailure(name, '命令可能产生副作用;请改用 Read/Glob/Grep 或只读查询命令')
|
|
716
|
-
: undefined;
|
|
704
|
+
return readOnlyFailure(name, '通用 Shell 无法保证严格只读;请改用 Read/Glob/Grep,或在获准执行的节点运行命令');
|
|
717
705
|
}
|
|
718
706
|
return ctx.tools.find((tool) => tool.name === name)?.isReadOnly !== true
|
|
719
707
|
? readOnlyFailure(name, '该工具不是只读工具')
|
|
@@ -42,7 +42,6 @@ import type {
|
|
|
42
42
|
import {
|
|
43
43
|
ALL_TOOLS,
|
|
44
44
|
createAgentRuntimeContext,
|
|
45
|
-
isReadOnlyShellCommand,
|
|
46
45
|
parseCheckString,
|
|
47
46
|
recordRead,
|
|
48
47
|
resolveProviderForModel,
|
|
@@ -640,8 +639,7 @@ export function createWorkflowContext(deps: ApiDeps): WorkflowContext {
|
|
|
640
639
|
const toolByName = new Map((ctx.tools ?? []).map((tool) => [tool.name, tool]));
|
|
641
640
|
const allowed = opts.toolMode === 'read-only'
|
|
642
641
|
? requested.filter((name) =>
|
|
643
|
-
name
|
|
644
|
-
name === 'PowerShell' ||
|
|
642
|
+
name !== 'Bash' && name !== 'PowerShell' &&
|
|
645
643
|
toolByName.get(name)?.isReadOnly === true)
|
|
646
644
|
: requested;
|
|
647
645
|
|
|
@@ -682,7 +680,7 @@ export function createWorkflowContext(deps: ApiDeps): WorkflowContext {
|
|
|
682
680
|
`本节点实际可用工具只有:${allowed.length ? allowed.join('、') : '无'}。`,
|
|
683
681
|
'宿主系统提示或既往历史中提及的其他工具不属于本节点,不得调用。',
|
|
684
682
|
opts.toolMode === 'read-only'
|
|
685
|
-
? '
|
|
683
|
+
? '本节点严格只读:不得创建、覆盖、修改、移动或删除文件;使用 Read/Glob/Grep 等只读工具,不提供 Bash/PowerShell 通用执行。完成调查后直接返回事实报告。'
|
|
686
684
|
: '',
|
|
687
685
|
].filter(Boolean).join('\n')
|
|
688
686
|
: '';
|
|
@@ -952,18 +950,9 @@ export function createWorkflowContext(deps: ApiDeps): WorkflowContext {
|
|
|
952
950
|
sensitivity: 'public' as const,
|
|
953
951
|
});
|
|
954
952
|
|
|
955
|
-
const readOnlyDenial = (name: string,
|
|
953
|
+
const readOnlyDenial = (name: string, _rawArgs: string) => {
|
|
956
954
|
if (name === 'Bash' || name === 'PowerShell') {
|
|
957
|
-
|
|
958
|
-
try {
|
|
959
|
-
const parsed = JSON.parse(rawArgs) as { command?: unknown };
|
|
960
|
-
if (typeof parsed.command === 'string') command = parsed.command;
|
|
961
|
-
} catch {
|
|
962
|
-
return readOnlyFailure(name, '命令参数不是合法 JSON');
|
|
963
|
-
}
|
|
964
|
-
return !command || !isReadOnlyShellCommand(command)
|
|
965
|
-
? readOnlyFailure(name, '命令可能产生副作用;请改用 Read/Glob/Grep 或只读查询命令')
|
|
966
|
-
: undefined;
|
|
955
|
+
return readOnlyFailure(name, '通用 Shell 无法保证严格只读;请改用 Read/Glob/Grep,或在获准执行的节点运行命令');
|
|
967
956
|
}
|
|
968
957
|
return ctx.tools.find((tool) => tool.name === name)?.isReadOnly !== true
|
|
969
958
|
? readOnlyFailure(name, '该工具不是只读工具')
|
|
@@ -32,6 +32,8 @@ export class ModelExecutionOutcome {
|
|
|
32
32
|
code: event.code ?? 'execution_error',
|
|
33
33
|
message: event.error,
|
|
34
34
|
};
|
|
35
|
+
if ((event.type === 'error' || event.type === 'interrupted') && event.usage)
|
|
36
|
+
this.usage = event.usage;
|
|
35
37
|
if (event.type === 'interrupted')
|
|
36
38
|
this.interrupted = true;
|
|
37
39
|
if (event.type === 'turn_done') {
|
|
@@ -40,8 +42,8 @@ export class ModelExecutionOutcome {
|
|
|
40
42
|
!event.completionStatus || event.completionStatus === 'completed';
|
|
41
43
|
if (!this.completed)
|
|
42
44
|
this.failure ??= {
|
|
43
|
-
code: 'delivery_incomplete',
|
|
44
|
-
message: '阶段未正常完成交付',
|
|
45
|
+
code: event.completionStatus === 'no_answer' ? 'no_answer' : 'delivery_incomplete',
|
|
46
|
+
message: event.completionStatus === 'no_answer' ? '模型没有产出最终答复;已有进展保留供恢复,不作为成功结果。' : '阶段未正常完成交付',
|
|
45
47
|
};
|
|
46
48
|
}
|
|
47
49
|
}
|
|
@@ -46,6 +46,7 @@ export class ModelExecutionOutcome {
|
|
|
46
46
|
code: event.code ?? 'execution_error',
|
|
47
47
|
message: event.error,
|
|
48
48
|
};
|
|
49
|
+
if ((event.type === 'error' || event.type === 'interrupted') && event.usage) this.usage = event.usage;
|
|
49
50
|
if (event.type === 'interrupted') this.interrupted = true;
|
|
50
51
|
if (event.type === 'turn_done') {
|
|
51
52
|
this.usage = event.usage;
|
|
@@ -53,8 +54,8 @@ export class ModelExecutionOutcome {
|
|
|
53
54
|
!event.completionStatus || event.completionStatus === 'completed';
|
|
54
55
|
if (!this.completed)
|
|
55
56
|
this.failure ??= {
|
|
56
|
-
code: 'delivery_incomplete',
|
|
57
|
-
message: '阶段未正常完成交付',
|
|
57
|
+
code: event.completionStatus === 'no_answer' ? 'no_answer' : 'delivery_incomplete',
|
|
58
|
+
message: event.completionStatus === 'no_answer' ? '模型没有产出最终答复;已有进展保留供恢复,不作为成功结果。' : '阶段未正常完成交付',
|
|
58
59
|
};
|
|
59
60
|
}
|
|
60
61
|
}
|
|
@@ -112,9 +112,11 @@ describe('零工具调用信号(announce-not-do)', () => {
|
|
|
112
112
|
expect(out).toBe('minor'); // 正常返回,没有因为零工具调用而失败
|
|
113
113
|
});
|
|
114
114
|
|
|
115
|
-
test('
|
|
115
|
+
test('空答明确失败,但不误报为 announce-not-do', async () => {
|
|
116
116
|
const h = await harness(textOnlyChat(' '));
|
|
117
|
-
await h.globals.model.agent('干点什么')
|
|
117
|
+
await expect(h.globals.model.agent('干点什么')).rejects.toMatchObject({
|
|
118
|
+
name: 'WorkflowModelError', code: 'no_answer', recovery: { partialText: '' },
|
|
119
|
+
});
|
|
118
120
|
expect(h.progress()).not.toContain('零工具调用');
|
|
119
121
|
});
|
|
120
122
|
|
|
@@ -226,7 +226,7 @@ test('read-only node hides write tools and rejects fabricated Write plus mutatin
|
|
|
226
226
|
if (round++ === 0) {
|
|
227
227
|
const names = requestTools?.map((tool) => tool.name) ?? [];
|
|
228
228
|
expect(names).toContain('Read');
|
|
229
|
-
expect(names).toContain('Bash');
|
|
229
|
+
expect(names).not.toContain('Bash');
|
|
230
230
|
expect(names).not.toContain('Write');
|
|
231
231
|
expect(JSON.stringify(messages)).toContain('工作流节点工具边界');
|
|
232
232
|
expect(JSON.stringify(messages)).toContain('本节点严格只读');
|
|
@@ -252,7 +252,6 @@ test('read-only node hides write tools and rejects fabricated Write plus mutatin
|
|
|
252
252
|
return;
|
|
253
253
|
}
|
|
254
254
|
const transcript = JSON.stringify(messages);
|
|
255
|
-
expect(transcript).toContain('当前工作流节点为只读');
|
|
256
255
|
expect(transcript).toContain('不在当前工作流节点的有效工具集中');
|
|
257
256
|
yield { type: 'text_delta', delta: '只读调查完成' };
|
|
258
257
|
yield { type: 'done', stopReason: 'stop' };
|
|
@@ -281,13 +280,13 @@ test('read-only node hides write tools and rejects fabricated Write plus mutatin
|
|
|
281
280
|
h.events.some(
|
|
282
281
|
(event) =>
|
|
283
282
|
event.type === 'warning' &&
|
|
284
|
-
String(event.message).includes('已移除写工具:Write'),
|
|
283
|
+
String(event.message).includes('已移除写工具:Bash、Write'),
|
|
285
284
|
),
|
|
286
285
|
).toBe(true);
|
|
287
286
|
await h.state.flush();
|
|
288
287
|
});
|
|
289
288
|
|
|
290
|
-
test('read-only node
|
|
289
|
+
test('strict read-only node rejects even apparently read-only shell commands', async () => {
|
|
291
290
|
let round = 0;
|
|
292
291
|
const executed: string[] = [];
|
|
293
292
|
const gateRequests: { name: string; isReadOnly: boolean }[] = [];
|
|
@@ -325,8 +324,8 @@ test('read-only node still executes Bash commands classified as read-only', asyn
|
|
|
325
324
|
isolated: true,
|
|
326
325
|
}),
|
|
327
326
|
).resolves.toBe('done');
|
|
328
|
-
expect(executed).toEqual([
|
|
329
|
-
expect(gateRequests).toEqual([
|
|
327
|
+
expect(executed).toEqual([]);
|
|
328
|
+
expect(gateRequests).toEqual([]);
|
|
330
329
|
await h.state.flush();
|
|
331
330
|
});
|
|
332
331
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { expect, test } from 'bun:test';
|
|
2
|
+
import { ModelExecutionOutcome, WorkflowModelError } from '../src/runtime/model.js';
|
|
3
|
+
|
|
4
|
+
test('no_answer fails with recovery evidence and never becomes a successful node value', () => {
|
|
5
|
+
const outcome = new ModelExecutionOutcome();
|
|
6
|
+
outcome.observe({ type: 'turn_done', completionStatus: 'no_answer' });
|
|
7
|
+
expect(outcome.completed).toBe(false);
|
|
8
|
+
try {
|
|
9
|
+
outcome.assertSuccess('已有进展', ['Read'], ['report.md']);
|
|
10
|
+
throw new Error('Expected failure');
|
|
11
|
+
} catch (error) {
|
|
12
|
+
expect(error).toBeInstanceOf(WorkflowModelError);
|
|
13
|
+
expect((error as WorkflowModelError).code).toBe('no_answer');
|
|
14
|
+
expect((error as WorkflowModelError).recovery).toMatchObject({ partialText: '已有进展', toolNames: ['Read'], artifacts: ['report.md'] });
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('an empty answer does not overwrite provider errors or cancellation', () => {
|
|
19
|
+
const failed = new ModelExecutionOutcome();
|
|
20
|
+
failed.observe({ type: 'error', code: 'llm_error', error: 'provider failed' });
|
|
21
|
+
failed.observe({ type: 'turn_done', completionStatus: 'no_answer' });
|
|
22
|
+
expect(failed.failure?.code).toBe('llm_error');
|
|
23
|
+
const cancelled = new ModelExecutionOutcome();
|
|
24
|
+
cancelled.observe({ type: 'interrupted', reason: 'manual' });
|
|
25
|
+
cancelled.observe({ type: 'turn_done', completionStatus: 'no_answer' });
|
|
26
|
+
expect(() => cancelled.assertSuccess('', [], [])).toThrow('阶段执行已中断');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('completed stays successful; other incomplete states keep their failure', () => {
|
|
30
|
+
const completed = new ModelExecutionOutcome();
|
|
31
|
+
completed.observe({ type: 'turn_done', completionStatus: 'completed' });
|
|
32
|
+
expect(() => completed.assertSuccess('报告', [], [])).not.toThrow();
|
|
33
|
+
const incomplete = new ModelExecutionOutcome();
|
|
34
|
+
incomplete.observe({ type: 'turn_done', completionStatus: 'delivery_incomplete' });
|
|
35
|
+
expect(incomplete.failure?.code).toBe('delivery_incomplete');
|
|
36
|
+
});
|
|
@@ -1,287 +1,47 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dynamic-ui
|
|
3
|
-
description:
|
|
3
|
+
description: 解释流程、架构、时序、状态关系时优先 Mermaid,定制布局或数据图用 SVG,替代大段 ASCII;适用于 Desktop/Web 回复和文档配图。CLI/TUI 直接显示用文字或表格,生成图形文件不受此限;简单回答不强制配图。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# 动态图示:先选载体,再选图形
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 判断是否需要图
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
不拥有任何专属渲染工具——本技能只产出一段 `<svg>...</svg>` 标记,由调用它的宿主或工具负责把这段
|
|
13
|
-
标记放到最终产出物里(聊天回复的围栏代码块,或者正在写的文档文件)。
|
|
10
|
+
图应让关系比文字更清楚,而不是装饰。简单事实、一步命令、短列表、清晰表格不画图。
|
|
11
|
+
保留用户指定 ASCII、原样代码/目录树和紧凑的 A → B 表达。正文用一句话保留图的关键结论。
|
|
14
12
|
|
|
15
|
-
##
|
|
13
|
+
## 输出载体
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
用一句正文保留图的关键结论,使无法看到图的用户仍能理解。图是解释,不是假按钮、执行状态或正式成果附件;真实状态来自工具事件,真实文件/网站仍按宿主交付机制提交。技能或图形渲染不可用时用文字/表格降级,不反复输出大段源码。
|
|
20
|
-
|
|
21
|
-
每轮系统上下文里都有一行「宿主: `<surface>`」(`desktop`/`tui`/`cli`/`headless`/`workflow`/`subagent`
|
|
22
|
-
之一),据此决定怎么交付:
|
|
23
|
-
|
|
24
|
-
- **`desktop`**(minimal-desktop 聊天界面):聊天气泡只认两种围栏代码块会被真正渲染——
|
|
25
|
-
` ```mermaid `(另一套机制,不归本技能管)和 ` ```svg `(本技能)。除此之外,直接写在正文里的
|
|
26
|
-
裸 `<svg>`/`<div>` 会被 Markdown 渲染器(`react-markdown` 开了 `skipHtml`)**静默丢弃**——不是
|
|
27
|
-
显示不出来,是从回复里彻底消失,绝不要这样做。判断值得画图时,直接输出**一个** ` ```svg ` 围栏
|
|
28
|
-
代码块;不要在围栏前后旁白"我先画一个坐标系"之类的过程说明,围栏本身就是最终产物。
|
|
29
|
-
- **`tui` / `cli` / `headless`**(终端):没有任何图形渲染能力,SVG 字节原样打印到终端只会是一堆
|
|
30
|
-
乱码,绝不能往回复文本里塞 `svg` 围栏。这些宿主下本技能只在**你正在用 Write / docx / pptx 等
|
|
31
|
-
工具落盘一份文档、报告、幻灯片、作业讲义**时起作用——继续读下面的设计规则,把 SVG 片段嵌进正在
|
|
32
|
-
生成的那份文件;如果只是普通问答且没有落盘目标,判断 Markdown 表格/文字是否已经够用,够用就
|
|
33
|
-
不要额外画图,这不是本技能能交付的场景。
|
|
34
|
-
- **`subagent` / `workflow`**:跟随发起它的宿主环境;拿不准时按"落盘文档"场景处理,不要把 SVG
|
|
35
|
-
写回文本回复。
|
|
36
|
-
|
|
37
|
-
## 与 ui-design 的分工——别重复造轮子
|
|
38
|
-
|
|
39
|
-
[ui-design](../ui-design/SKILL.md) 是这个项目里"样式与视觉设计"决策的统一入口:任何涉及整份
|
|
40
|
-
文档/整套 deck 的配色、VI、字体字阶、版式布局,都应该先去那里定下来。本技能的职责窄得多:
|
|
41
|
-
**这段内容值不值得画一张图、该画哪种图、怎么保证画出来的东西不重叠不脏**——是排版执行细节,
|
|
42
|
-
不是设计系统决策。
|
|
43
|
-
|
|
44
|
-
具体到颜色:下面「视觉 Token」表里的十六进制都是**兜底色**,不是权威色板。写文档、报告、幻灯片
|
|
45
|
-
时,如果 ui-design 已经为这份产出物定出了品牌色,直接把兜底色整体换成那份色值;只有在 Desktop
|
|
46
|
-
聊天场景(没有独立文档、没有 ui-design 介入的必要)才直接用表里给的兜底色。
|
|
47
|
-
|
|
48
|
-
## 输出契约
|
|
49
|
-
|
|
50
|
-
**两个宿主的根节点要求不一样,别弄混**:
|
|
51
|
-
|
|
52
|
-
- **Desktop 内联围栏**(```svg 围栏块):内容可以是一段纯 `<svg>...</svg>`,也可以是一段
|
|
53
|
-
静态的 HTML 卡片片段(比如 `comparison-cards` 那种带分隔线的卡片、徽标)——渲染层
|
|
54
|
-
(`SvgWidget.tsx`)只要求内容"看起来是标记"(以 `<` 开头),不强制根标签必须是
|
|
55
|
-
`<svg>`。两种情况都会被塞进一个独立的 Shadow DOM 宿主,类名不会跟同一会话里的其它围栏块
|
|
56
|
-
冲突。
|
|
57
|
-
- **写进文档**(docx/pptx/独立 html 报告,当图片资源嵌入):**必须是单一根节点的
|
|
58
|
-
`<svg>...</svg>`**——Word/PowerPoint 的图片嵌入管线需要一段真正的矢量图形,塞一段
|
|
59
|
-
`<div>` 卡片布局进去没有任何渲染器认得。如果一份文档里想要"卡片对比"这种排版,直接用
|
|
60
|
-
文档格式自己的表格/形状/文本框(参考 [ui-design](../ui-design/SKILL.md)/
|
|
61
|
-
[pptx](../pptx/SKILL.md)/[docx](../docx/SKILL.md)),不要试图把 HTML 卡片硬塞成"SVG"。
|
|
62
|
-
素材库里 `tree-flow`/`sequence-diagram` 这类图表类模板内部本来就是一个独立的
|
|
63
|
-
`<svg>...</svg>` 子树(外层 `<div class="widget">` 只是 Desktop 卡片外壳),写文档时把
|
|
64
|
-
这段内部 `<svg>` 单独抽出来即可;`comparison-cards`/`architecture-elements` 这类以 HTML
|
|
65
|
-
卡片为主的模板目前只适合 Desktop 内联场景,不要抽取硬塞进文档。
|
|
66
|
-
|
|
67
|
-
不管落到哪种根节点,都不要 `<!DOCTYPE>`、`<html>`、`<head>`、`<body>`、注释、说明性文字混进
|
|
68
|
-
这段标记内部。
|
|
69
|
-
|
|
70
|
-
**没有任何一个宿主能执行 `<script>`**:Desktop 的 CSP 是 `script-src 'self'`,内联脚本和
|
|
71
|
-
`onXxx` 事件属性本来就跑不起来;静态文档更不必说。因此:
|
|
72
|
-
- 不产出 `<script>` 标签、不产出 `onclick`/`onload` 这类事件属性。
|
|
73
|
-
- 不依赖 hover/click 才能看到的信息——数值、标签、图例这些关键信息必须直接画在图上常驻可见,
|
|
74
|
-
不能只在假想的交互态里出现。原技能里"局部交互/tooltip/toggle"的设想在这两个宿主下都不成立,
|
|
75
|
-
见 `scenes/micro-interaction.md` 里的替代做法。
|
|
76
|
-
|
|
77
|
-
## 视觉 Token
|
|
78
|
-
|
|
79
|
-
角色化 CSS 自定义属性,统一写法是 `var(--dui-<角色>, <字面量兜底值>)`。Desktop 环境下
|
|
80
|
-
`--dui-*` 已经在 App 全局样式(`global.css`)里定义并跟随亮暗主题;脱离 Desktop 时(独立 SVG
|
|
81
|
-
片段、嵌进文档、被人复制到别处)这些变量本来就不存在,`var()` 会直接落到兜底值——两种场景
|
|
82
|
-
零特判,不要为文档场景另写一套字面量色板,只需要在有 ui-design 品牌色时替换兜底值本身。
|
|
83
|
-
|
|
84
|
-
| 角色 | 写法 | 兜底值(= App 深色主题) | 用途 |
|
|
85
|
-
|---|---|---|---|
|
|
86
|
-
| 正文文字 | `var(--dui-text, #f4f1e7)` | #f4f1e7 | 图上主要文字 |
|
|
87
|
-
| 次要文字 | `var(--dui-text-muted, #9fb0ae)` | #9fb0ae | 元数据、图例、说明、坐标轴 |
|
|
88
|
-
| 弱化文字 | `var(--dui-text-faint, #6f8280)` | #6f8280 | 占位、禁用态、次要连线 |
|
|
89
|
-
| 边框 | `var(--dui-border, rgba(238,241,235,.14))` | — | 卡片/面板描边 |
|
|
90
|
-
| 表面 | `var(--dui-surface, #172321)` | — | 卡片/面板填充 |
|
|
91
|
-
| 次表面 | `var(--dui-surface-muted, #1e2c29)` | — | 嵌套区域 |
|
|
92
|
-
| 品牌色 | `var(--dui-brand, #c85f3d)` | — | 主焦点、主路径、推荐项 |
|
|
93
|
-
| 强调色 | `var(--dui-accent, #2bb8c4)` | — | 次焦点、第二类目 |
|
|
94
|
-
| 成功 | `var(--dui-success, #2bb8c4)` | — | 仅状态/风险语义时用 |
|
|
95
|
-
| 警示 | `var(--dui-warning, #d9b354)` | — | 同上 |
|
|
96
|
-
| 危险 | `var(--dui-danger, #d9645a)` | — | 同上 |
|
|
97
|
-
| 系列 1 | `var(--dui-chart-series-1, #c85f3d)` | = 品牌色 | 同类并列数据源第 1 项 |
|
|
98
|
-
| 系列 2 | `var(--dui-chart-series-2, #2bb8c4)` | | 第 2 项 |
|
|
99
|
-
| 系列 3 | `var(--dui-chart-series-3, #d9b354)` | | 第 3 项 |
|
|
100
|
-
| 系列 4 | `var(--dui-chart-series-4, #6fa87c)` | | 第 4 项 |
|
|
101
|
-
| 系列其它 | `var(--dui-chart-other, #6f8280)` | | Top N + Other 的 Other |
|
|
102
|
-
|
|
103
|
-
**品牌基线**:不默认紫色——那是原技能抄来的,不是这个项目的品牌。Desktop 环境下品牌色自动是
|
|
104
|
-
App 自己的铁锈橙(`--accent`);写文档时如果 ui-design 给了别的品牌色,把上表兜底值整体替换,
|
|
105
|
-
不要还留着铁锈橙或紫色。
|
|
106
|
-
|
|
107
|
-
字体:`font-family: var(--font, -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif)`
|
|
108
|
-
(正文),`var(--mono, "Cascadia Code", "JetBrains Mono", Consolas, monospace)`(数值、代码风格
|
|
109
|
-
标识)。
|
|
110
|
-
|
|
111
|
-
间距/圆角/字号是纯字面量,不必变量化跨主题(结构性数值本来就不该跟着主题换):
|
|
112
|
-
- 间距:4 / 8 / 12 / 16 / 20 / 24px,用于 gap、padding、图例行距。
|
|
113
|
-
- 圆角:8px 默认;12px 卡片;999px 胶囊/圆点。
|
|
114
|
-
- 字号:12/18(说明文字,最小不低于 11px)、14/20(正文基线)、16/24(标题,不能再大)。
|
|
115
|
-
|
|
116
|
-
## Scope
|
|
117
|
-
|
|
118
|
-
用于:数值趋势、量级对比、构成/占比、分布、流程强度;模块关系、依赖图、流程图、状态转移、
|
|
119
|
-
调用链、时间线/排期;方案对比、技术选型、风险摘要、决策矩阵;原理讲解、因果链;需要展示"切换前
|
|
120
|
-
后/开关两态"这类状态对比但做不到真交互的场景(见 `scenes/micro-interaction.md`)。
|
|
121
|
-
|
|
122
|
-
不用于:独立可运行的网站/App/交互原型(用 [ui-design](../ui-design/SKILL.md) 或
|
|
123
|
-
[web-artifacts-builder](../web-artifacts-builder/SKILL.md));一次性事实、一步命令、Markdown
|
|
124
|
-
表格已经足够清楚的内容;没有明确焦点的装饰性配图;`tui`/`cli`/`headless` 下没有落盘目标的纯
|
|
125
|
-
聊天场景。
|
|
126
|
-
|
|
127
|
-
## File Map
|
|
128
|
-
|
|
129
|
-
`{{SKILL_DIR}}` 是本 `SKILL.md` 所在目录。
|
|
130
|
-
|
|
131
|
-
```
|
|
132
|
-
{{SKILL_DIR}}/
|
|
133
|
-
├── SKILL.md ← 唯一入口(本文件)
|
|
134
|
-
├── scenes/ ← 场景指南(每个场景一个文件)
|
|
135
|
-
│ ├── data-visualization.md
|
|
136
|
-
│ ├── architecture-and-flow.md
|
|
137
|
-
│ ├── comparison-and-decision.md
|
|
138
|
-
│ ├── mechanism-explanation.md
|
|
139
|
-
│ └── micro-interaction.md
|
|
140
|
-
├── tokens/
|
|
141
|
-
│ └── visual-tokens.md ← 完整 token 定义(按需读取)
|
|
142
|
-
└── templates/ ← 素材库(可复用原型示例)
|
|
143
|
-
├── manifest.json
|
|
144
|
-
└── <template-id>/
|
|
145
|
-
├── template.md ← 素材说明(20-30 行)
|
|
146
|
-
├── widget-code.html ← 完整可用示例(纯 <svg>,无 <script>)
|
|
147
|
-
└── fixture.json ← 示例数据
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
`templates/manifest.json` 里 `status` 字段如实标注:`ready` 表示已按本文档的静态 SVG 契约验证
|
|
151
|
-
可用;`needs-static-rewrite` 表示 `widget-code.html` 还是旧版 Chart.js/`<script>` 悬浮提示交互
|
|
152
|
-
实现,两个宿主都执行不了脚本,**暂不可用**——按 `template.md` 记录的设计意图手工画静态 SVG,
|
|
153
|
-
或改用下面的回退基元,不要因为目录存在就当它能直接抄。
|
|
154
|
-
|
|
155
|
-
## Visual Design 核心原则
|
|
156
|
-
|
|
157
|
-
- **无缝** —— 用户不该看出宿主界面在哪结束、你画的图从哪开始。
|
|
158
|
-
- **扁平** —— 不用渐变、噪点纹理、装饰性阴影,干净的平面。
|
|
159
|
-
- **紧凑** —— 内联展示最关键的信息,其余留给正文文字。
|
|
160
|
-
- **分离** —— 解释性文字放正文,图上只放视觉证据。
|
|
161
|
-
- **图表就是图表** —— 图表类 widget 只画图表本身、可选的简短标题、以及坐标轴/图例/标签这些必要
|
|
162
|
-
的读图辅助;不要在图表 widget 里塞洞见、结论、建议或页脚文案。
|
|
163
|
-
|
|
164
|
-
素材源:先读 `templates/manifest.json`,再读选中的 `templates/<id>/template.md`,再决定是否
|
|
165
|
-
借用 `widget-code.html`/`fixture.json`。素材是参考范例,不是死板的固定布局。
|
|
166
|
-
|
|
167
|
-
兜底规则:没有现成素材匹配时,自定义输出也必须选定一种紧凑原型(`chart-card`、
|
|
168
|
-
`metric-strip-chart`、`node-flow`、`decision-cards`、`compact-table-visual`、
|
|
169
|
-
`explanation-panel` 之一),并遵守与素材相同的 token、表面、密度、颜色优先级、字体、间距、
|
|
170
|
-
圆角、焦点规则。自定义输出不能为了凑内容而发明新色板、新字号阶梯、新间距刻度、新圆角值。
|
|
171
|
-
对于 SVG 流程图、架构图、框架图、关系图,只有在先做过一次简单的坐标规划、确认节点/标签/
|
|
172
|
-
连线轨道/图例/徽标都能不重叠地放下之后,才允许走这条兜底路线。
|
|
173
|
-
|
|
174
|
-
**焦点色**:颜色契约的优先级高于语义色猜测。先用中性表面/连线加 `--dui-brand`/
|
|
175
|
-
`--dui-chart-series-*`;只有用户明确要求状态/风险/健康度编码,或数据字段本身就是状态/风险
|
|
176
|
-
变量时,才用 `--dui-accent` 或语义色。"success/failure/warning/blocked/accepted/rejected"
|
|
177
|
-
这类词本身不能自动带出绿/黄/红。
|
|
178
|
-
|
|
179
|
-
**图表系列色**:同类数据源统一用 `--dui-chart-series-1` 到 `-4`;溢出/其它用
|
|
180
|
-
`--dui-chart-other`。不要给普通数据源用彩虹色、语义色。5 个以上同级数据源应归组为 Top N +
|
|
181
|
-
Other、拆成小倍图、或改用表格/列表。
|
|
182
|
-
|
|
183
|
-
**图示分类**:结构用中性色,焦点/推荐用品牌色,次要类目用强调色。普通流程图/架构图/框架图/
|
|
184
|
-
依赖图优先用中性+品牌色或图表系列色,而不是语义色。一张紧凑图最多 1-2 个有含义的颜色。连线
|
|
185
|
-
本身不应用图示分类色。
|
|
186
|
-
|
|
187
|
-
**连线**:默认中性 `--dui-text-faint` 或 `--dui-border`;主路径用 `--dui-brand`;只有明确标注
|
|
188
|
-
的并列路径才用 `--dui-chart-series-*`;只有状态/风险是主要编码变量时才用语义连线色。流程图连线
|
|
189
|
-
用平滑曲线/圆角折线,`stroke-linecap="round"`,`stroke-linejoin="round"`。一张图最多 2 种连线
|
|
190
|
-
样式。方向箭头用统一的 marker:`viewBox="0 0 8 8"`、`refX="7"`、`refY="4"`、
|
|
191
|
-
`markerWidth="8"`、`markerHeight="8"`、`markerUnits="userSpaceOnUse"`、`orient="auto"`,箭头
|
|
192
|
-
路径 `M1 1 L7 4 L1 7 Z`,配合 `marker-end` 挂在代表连线本身的那条 path 上——不要用花括号、
|
|
193
|
-
双曲线、独立三角形拼箭头。
|
|
194
|
-
|
|
195
|
-
## Content Rules
|
|
196
|
-
|
|
197
|
-
每张图只要一个清晰可见的焦点(推荐项、关键路径、瓶颈、最高值、阶段边界、风险标记),用位置、
|
|
198
|
-
标签或一个强调色让它显眼,不要只靠颜色区分。
|
|
199
|
-
|
|
200
|
-
文案密度:节点标签 2-5 个字/词;卡片标题 6-10 个字/词;副标题 ≤5 个字/词;连线标签 1-3 个字/
|
|
201
|
-
词,能省则省。
|
|
202
|
-
|
|
203
|
-
复杂度预算:1 个焦点,2-5 个主节点,2-4 个选项/KPI/系列,最多 1 个交互概念(在两个宿主下等价于
|
|
204
|
-
"最多一种状态对比",见 micro-interaction)。横向一排最多 4 个方框,5 个以上要归组/换行/拆分。
|
|
205
|
-
自定义 SVG 图示保持 ≤6 个未分组节点、≤8 条连线,更大的结构要归组、拆面板、改表格,或直接用
|
|
206
|
-
Markdown 回答。
|
|
207
|
-
|
|
208
|
-
数据诚实:估算值要标注"约/估计";数值保留一致的小数位;数字旁标单位;来源本身是粗略估计时不要
|
|
209
|
-
显得像精确值。绝不画地图——按区域用条形图/表格/矩阵代替。
|
|
210
|
-
|
|
211
|
-
## SVG Fundamentals
|
|
212
|
-
|
|
213
|
-
出现 SVG、连线、流程图、架构图、结构图、机制示意图时都适用下面的几何规则。
|
|
214
|
-
|
|
215
|
-
**ViewBox**:优先 `viewBox="0 0 720 H"`,`width="100%"`,`height="auto"`。安全边距 ≥40 设计
|
|
216
|
-
单位或 viewBox 宽度的 6%(取更大值)。`H` 由最低可见图形/文字基线/连线加底部留白决定。不要用
|
|
217
|
-
负坐标、不要为了塞下过长标签而收缩 viewBox、不要在底部留大片无法解释的空白、不要靠裁剪/背景块
|
|
218
|
-
掩盖不合法的几何。
|
|
219
|
-
|
|
220
|
-
**文字尺寸**:标签 14px(拉丁字符约 8px/字宽),元数据 12px(约 7px/字宽)。节点宽度 ≥
|
|
221
|
-
max(标题字数×8, 副标题字数×7) + 24px 内边距。中文标签要比可用宽度短。代码风格字符串、数学
|
|
222
|
-
符号、长标识符、混合文字要按 30-50% 放大宽度估算。
|
|
223
|
-
|
|
224
|
-
**硬性拟合门槛**:在给每个可见文字标签、节点、连线标签、图例项、徽标、容器都估算出包围盒之前,
|
|
225
|
-
不要画自定义 SVG。任何一个盒子放不下就先削减内容:缩短标签、归组节点、拆分图示、改成表格/列表,
|
|
226
|
-
或直接用 Markdown 回答。不要用更小的字号、透明重叠、`overflow: hidden`、裁剪蒙版或背景矩形来
|
|
227
|
-
替代一次真正合法的布局。
|
|
228
|
-
|
|
229
|
-
**连线**:每条 path/polyline 都要 `fill="none"`,绕开无关节点。同一行相邻节点检查
|
|
230
|
-
`left.x + left.width + gap <= right.x`,无关节点间距 ≥32,流程步骤间距 ≥60;同一列相邻节点
|
|
231
|
-
检查 `top.y + top.height + gap <= bottom.y`,堆叠节点间距 ≥28,带连线标签时 ≥56。结构容器要
|
|
232
|
-
先于内部节点放置,且必须留出可见内边距、不与标题/图例/外部输入输出碰撞。
|
|
233
|
-
|
|
234
|
-
**画之前的静默自检**:viewBox 高度按最低元素+留白算出;所有内容落在 x=0..720 内;每个文字
|
|
235
|
-
标签都留了内边距;任何带文字的 `<rect>`/面板/卡片/图例都按内容尺寸撑开容器并留出内边距;没有
|
|
236
|
-
两个节点/标签/徽标/图例/连线标签的包围盒重叠;没有连线穿过无关节点或文字;流程图节点用圆角
|
|
237
|
-
矩形,判断/特殊形状用软化路径。以上任意一条不满足,就不要输出这段 SVG——简化视觉,或改用文字/
|
|
238
|
-
表格兜底。
|
|
239
|
-
|
|
240
|
-
## Scene Router
|
|
241
|
-
|
|
242
|
-
先判断用户意图,路由到**一个**场景文件:
|
|
243
|
-
|
|
244
|
-
| 用户意图 | 场景文件 |
|
|
15
|
+
| 最终阅读载体 | 输出方式 |
|
|
245
16
|
|---|---|
|
|
246
|
-
|
|
|
247
|
-
|
|
|
248
|
-
|
|
|
249
|
-
|
|
|
250
|
-
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
素材意图速查:`line-trend` 趋势;`bar-chart-multiple` 分组对比;`scatter-chart` 相关性/离群点;
|
|
278
|
-
`gantt-chart` 排期/关键路径;`funnel-bar-chart` 转化流失;`sankey-chart` 带权重的分阶段流动;
|
|
279
|
-
`heatmap-chart` 密度;`pie-donut-text` 带总计的构成;`bar-stacked-legend` 堆叠贡献;
|
|
280
|
-
`pie-chart-label-list` 带标签的部分-整体;`radar-chart-legend`/`radar-chart-lines-only` 画像
|
|
281
|
-
对比;`comparison-cards` 方案权衡;`sequence-diagram` 有序调用;`tree-flow` 层级;
|
|
282
|
-
`architecture-elements` 架构原语。
|
|
283
|
-
|
|
284
|
-
新增、改名、删除或调整某个素材的路由意图时,`templates/<template-id>/template.md`、
|
|
285
|
-
`widget-code.html`、`fixture.json`、`templates/manifest.json`、相关 `scenes/*.md`、
|
|
286
|
-
`templates/README.md` 要一起改。目录或必需文件缺失就不能标 `ready`。兜底原型不是素材 id,
|
|
287
|
-
不应出现在 `templates/manifest.json` 里。
|
|
17
|
+
| Desktop/Web 聊天 | `mermaid` 或 `svg` 围栏;不输出正文裸 SVG/HTML,Markdown 会忽略它们 |
|
|
18
|
+
| CLI/TUI 直接阅读 | 文字、表格、紧凑 ASCII;不把 Mermaid/SVG 源码当可见图 |
|
|
19
|
+
| 文档、网页、图形文件(包括 CLI 发起) | Mermaid/SVG;目标不渲染 Mermaid 时先转为 SVG/PNG 再嵌入 |
|
|
20
|
+
| headless/workflow/subagent | 跟随最终阅读载体,不把运行入口当成输出格式;保持结构化协议契约 |
|
|
21
|
+
| 载体未知 | 保留文字结论,按请求交付图形源码/文件,不声称已渲染 |
|
|
22
|
+
|
|
23
|
+
不创建无关文件。用户要文件时交付真实路径/附件;内联图不是假按钮、执行状态或虚构成果。
|
|
24
|
+
不宣称渲染器会自动把错误反馈给 Agent;只有实际观察或工具检查才算验证。
|
|
25
|
+
|
|
26
|
+
## 图形选择:只读所选分支
|
|
27
|
+
|
|
28
|
+
1. **流程、架构、依赖、时序、状态、层级**:默认 Mermaid,读取 [mermaid.md](mermaid.md)。
|
|
29
|
+
不需要读取 SVG 模板、颜色表或手算节点坐标。
|
|
30
|
+
2. **精确布局、机制示意、定制数据图**:选 SVG,读取 [svg-design.md](svg-design.md),
|
|
31
|
+
再按该文件选择一个场景/所需素材。简单 Mermaid 能清楚表达时不升级到手写 SVG。
|
|
32
|
+
3. **精确数值比较**:先考虑表格;需要趋势、分布或空间关系时才画图。
|
|
33
|
+
4. **网站/App 的真实交互**:使用 [ui-design](../ui-design/SKILL.md),不把静态图伪装成应用。
|
|
34
|
+
|
|
35
|
+
复杂图先分组或拆分,保留关键节点和方向,不靠小字号塞满屏幕。
|
|
36
|
+
缺少可用渲染器或图连续失败时保留文字/表格,不反复倾倒源码。
|
|
37
|
+
|
|
38
|
+
## 交付与检查
|
|
39
|
+
|
|
40
|
+
- Desktop/Web 用对应语言围栏,图下简述结论,不重复展示一份源码。
|
|
41
|
+
- SVG 不含脚本、事件处理器、外部资源或需要点击才能看见的关键信息。
|
|
42
|
+
新图优先真正的 SVG;旧静态 HTML 卡片兼容性不意味着 HTML 是通用图片格式。
|
|
43
|
+
- 文档中的图片必须是真正的 SVG 或 PNG,不把 HTML 卡片当 SVG。
|
|
44
|
+
独立 SVG 导出须将 CSS 主题变量解析为实际色值与字体;目标转换器不兼容时提供 PNG。
|
|
45
|
+
- 有渲染工具时检查真实渲染:标签完整、无重叠、明暗主题可读、窄屏不裁切。
|
|
46
|
+
没有验证条件则如实说明,不能把语法看似正确当作渲染成功。
|
|
47
|
+
- 下载或预览依赖宿主真实成果机制,不能用画出来的下载按钮替代。
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Mermaid 轻量路径
|
|
2
|
+
|
|
3
|
+
适合结构和关系,不套用 SVG 的模板选择或坐标规划。宿主已有 Mermaid 渲染器,
|
|
4
|
+
不为聊天图另行安装库。使用标准语法,避免依赖特定版本的新图种。
|
|
5
|
+
|
|
6
|
+
- 流程/架构:`flowchart TD` 或 `flowchart LR`;窄屏优先纵向。
|
|
7
|
+
- 交互时序:`sequenceDiagram`。
|
|
8
|
+
- 状态转换:`stateDiagram-v2`。
|
|
9
|
+
- 实体关系:`erDiagram`;需要精确布局或渲染器不支持时改 SVG/表格。
|
|
10
|
+
- 节点使用短稳定 ASCII ID,显示标签使用带引号的中文文本。
|
|
11
|
+
- 标签短句,不嵌入 HTML、脚本、click 回调、外部图片或 init 配置覆盖宿主安全与主题设置。
|
|
12
|
+
- 单图只表达一个问题;大图按阶段或职责拆分,避免横向长链。
|
|
13
|
+
- 不强制配色,优先宿主主题,不画假的实时状态或操作按钮。
|
|
14
|
+
|
|
15
|
+
Desktop/Web 回复示例(正文保留结论):
|
|
16
|
+
|
|
17
|
+
人类确认目标后,Agent 执行并交由人类验收。
|
|
18
|
+
|
|
19
|
+
```mermaid
|
|
20
|
+
flowchart TD
|
|
21
|
+
goal["确认目标"] --> work["Agent 执行"]
|
|
22
|
+
work --> review["人类验收"]
|
|
23
|
+
review -->|需要修改| work
|
|
24
|
+
review -->|通过| done["交付成果"]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
检查节点、箭头方向、分支标签与事实一致。可用渲染工具时实际渲染,
|
|
28
|
+
失败先简化语法/标签,不无限重试;仍失败就用 SVG 或文字/表格。
|
|
29
|
+
没有渲染证据不能声称已验证。
|
|
30
|
+
|
|
31
|
+
生成文档时保存源码仅在需要时进行;不支持 Mermaid 的阅读器需先将图渲染成 SVG/PNG,
|
|
32
|
+
然后用目标文档的图片机制嵌入并检查成品。不要直接粘贴围栏就称完成配图。
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# 架构与流程场景
|
|
2
2
|
|
|
3
|
+
默认先走 `../mermaid.md`,用 Mermaid 表达流程、架构、时序和状态关系。
|
|
4
|
+
只有需要定制 SVG 布局时才执行下方 SVG 素材与坐标规则,并读取 `../svg-design.md`。
|
|
5
|
+
|
|
3
6
|
共用契约:`SKILL.md`、本文件、`templates/manifest.json`、选中的 `templates/<id>/template.md`、
|
|
4
7
|
`tokens/visual-tokens.md`。
|
|
5
8
|
|
|
@@ -48,7 +51,7 @@
|
|
|
48
51
|
素材。
|
|
49
52
|
5. 边界符合时用 `sequence-diagram`、`tree-flow`、`architecture-elements`。
|
|
50
53
|
6. 只有边界不匹配、关系规模超出素材能力,或 Markdown 明显更清楚时才否掉一个现成素材
|
|
51
|
-
(`gantt-chart` 目前是 `needs-static-rewrite`,暂不可用,见
|
|
54
|
+
(`gantt-chart` 目前是 `needs-static-rewrite`,暂不可用,见 `../svg-design.md`)。
|
|
52
55
|
7. 没有素材匹配时选 `node-flow` 或 `explanation-panel`,再套用下面的 SVG 布局规则。
|
|
53
56
|
8. 画之前先选定一种骨架:横向流程、纵向流程、泳道/时序、分组架构,或排期——不要混用骨架,
|
|
54
57
|
除非每种各占一个独立面板。
|