fix: complete whiteboard skill examples
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source_hash": "sha256:98a61c88e23f4c223f4aae341f0d812545f6154cf18736e1f90bed88b6b47bca",
|
||||
"source_hash": "sha256:2fb34ad56cc801af92a057aeda1b8af7e886f6d129bedb643b8d08e4f59c5177",
|
||||
"surface_hash": "sha256:942309e0671ad009203266b2d6dade23a0afbbda5de77df393964b0e5cef39f6",
|
||||
"coverage": {
|
||||
"surface_products": 27,
|
||||
|
||||
@@ -278,7 +278,8 @@
|
||||
"dws-wukong@e2da8ab947c6:wukong/products/whiteboard.go",
|
||||
"internal/cli/schema_hints/metadata/whiteboard.json",
|
||||
"internal/cli/schema_hints/selection/whiteboard.json",
|
||||
"skills/mono/references/products/whiteboard/open-nodes-v1/02-query.md"
|
||||
"skills/mono/references/products/whiteboard/open-nodes-v1/02-query.md",
|
||||
"skills/mono/references/products/whiteboard/recipes.md"
|
||||
],
|
||||
"use_when": [
|
||||
"已知承载文档 nodeId 和白板 partId,需要检查当前白板节点、布局或写入支持时"
|
||||
@@ -363,6 +364,12 @@
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Migrated from dws-wukong e2da8ab947c6 and tightened for the public command-level safety model: every remote whiteboard update requires --yes."
|
||||
},
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "risk-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -379,6 +386,12 @@
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Migrated from dws-wukong e2da8ab947c6 and tightened for the public command-level safety model: every remote whiteboard update requires --yes."
|
||||
},
|
||||
{
|
||||
"value": "write",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -415,6 +428,12 @@
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Migrated from dws-wukong e2da8ab947c6 and tightened for the public command-level safety model: every remote whiteboard update requires --yes."
|
||||
},
|
||||
{
|
||||
"value": "unknown",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -502,6 +521,12 @@
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Migrated from dws-wukong e2da8ab947c6 and tightened for the public command-level safety model: every remote whiteboard update requires --yes."
|
||||
},
|
||||
{
|
||||
"value": "medium",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -536,7 +561,8 @@
|
||||
"cobra-help:dws whiteboard update",
|
||||
"dws-wukong@e2da8ab947c6:wukong/products/whiteboard.go",
|
||||
"internal/cli/schema_hints/metadata/whiteboard.json",
|
||||
"internal/cli/schema_hints/selection/whiteboard.json"
|
||||
"internal/cli/schema_hints/selection/whiteboard.json",
|
||||
"skills/mono/references/products/whiteboard/recipes.md"
|
||||
],
|
||||
"use_when": [
|
||||
"已有 nodeId、partId 和合规 OpenNodes V1 文件,用户确认后要追加图形、文本、连接线或整页替换时"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source_hash": "sha256:98a61c88e23f4c223f4aae341f0d812545f6154cf18736e1f90bed88b6b47bca",
|
||||
"source_hash": "sha256:2fb34ad56cc801af92a057aeda1b8af7e886f6d129bedb643b8d08e4f59c5177",
|
||||
"surface_hash": "sha256:942309e0671ad009203266b2d6dade23a0afbbda5de77df393964b0e5cef39f6",
|
||||
"source_files": 174,
|
||||
"source_files": 175,
|
||||
"hint_files": 56,
|
||||
"hint_products": 44,
|
||||
"hint_tools": 1860,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"surface_hash": "sha256:942309e0671ad009203266b2d6dade23a0afbbda5de77df393964b0e5cef39f6",
|
||||
"source_hash": "sha256:ed39f80f8291a9e3098941cfde122cd536b574a50614fbbff302dbb6a8653ead",
|
||||
"source_hash": "sha256:f9e4ac0213cd4863b20cfb1af7d680662070020c4e2be2d9aa40fce3b7ea5702",
|
||||
"catalog": {
|
||||
"agent_metadata": {
|
||||
"products_with_metadata": 27,
|
||||
"source": "embedded-skill-metadata",
|
||||
"source_hash": "sha256:98a61c88e23f4c223f4aae341f0d812545f6154cf18736e1f90bed88b6b47bca",
|
||||
"source_hash": "sha256:2fb34ad56cc801af92a057aeda1b8af7e886f6d129bedb643b8d08e4f59c5177",
|
||||
"surface_hash": "sha256:942309e0671ad009203266b2d6dade23a0afbbda5de77df393964b0e5cef39f6",
|
||||
"surface_products": 27,
|
||||
"surface_tools": 854,
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
"dws-wukong@e2da8ab947c6:wukong/products/whiteboard.go",
|
||||
"internal/cli/schema_hints/metadata/whiteboard.json",
|
||||
"internal/cli/schema_hints/selection/whiteboard.json",
|
||||
"skills/mono/references/products/whiteboard/open-nodes-v1/02-query.md"
|
||||
"skills/mono/references/products/whiteboard/open-nodes-v1/02-query.md",
|
||||
"skills/mono/references/products/whiteboard/recipes.md"
|
||||
],
|
||||
"agent_summary": "读取钉钉文档内已有白板的 OpenNodes 内容",
|
||||
"agent_summary_source": "dws-agent-selection/whiteboard",
|
||||
@@ -556,7 +557,8 @@
|
||||
"cobra-help:dws whiteboard update",
|
||||
"dws-wukong@e2da8ab947c6:wukong/products/whiteboard.go",
|
||||
"internal/cli/schema_hints/metadata/whiteboard.json",
|
||||
"internal/cli/schema_hints/selection/whiteboard.json"
|
||||
"internal/cli/schema_hints/selection/whiteboard.json",
|
||||
"skills/mono/references/products/whiteboard/recipes.md"
|
||||
],
|
||||
"agent_summary": "经用户确认后向已有白板追加 OpenNodes 或整页重建",
|
||||
"agent_summary_source": "dws-agent-selection/whiteboard",
|
||||
@@ -660,6 +662,12 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/whiteboard.json",
|
||||
"value": "user_required"
|
||||
},
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"selected": false,
|
||||
"source": "risk-default",
|
||||
"value": "not_required"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -690,6 +698,12 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/whiteboard.json",
|
||||
"value": "write"
|
||||
},
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"selected": false,
|
||||
"source": "command-verb",
|
||||
"value": "write"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -726,6 +740,12 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/whiteboard.json",
|
||||
"value": "unknown"
|
||||
},
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"selected": false,
|
||||
"source": "effect-default",
|
||||
"value": "unknown"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -813,6 +833,12 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/whiteboard.json",
|
||||
"value": "high"
|
||||
},
|
||||
{
|
||||
"precedence": "inference_or_default",
|
||||
"selected": false,
|
||||
"source": "effect-default",
|
||||
"value": "medium"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
OpenNodes V1 的完整字段、节点类型、目录枚举和错误语义按需读取
|
||||
[协议索引](./whiteboard/open-nodes-v1.md);不要根据本页概要猜测节点字段或
|
||||
`geometry`、`catalogId` 等枚举值。
|
||||
`geometry`、`catalogId` 等枚举值。渐变卡片、Frame 分支、SVG/Vector 等完整
|
||||
工作流见 [常用 Recipes](./whiteboard/recipes.md)。
|
||||
|
||||
## 标准流程
|
||||
|
||||
@@ -24,7 +25,24 @@ OpenNodes V1 的完整字段、节点类型、目录枚举和错误语义按需
|
||||
"source": {
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": [{"id":"n1","type":"text","x":40,"y":40,"text":"方案"}]
|
||||
"nodes": [
|
||||
{
|
||||
"id": "n1",
|
||||
"type": "text",
|
||||
"x": 40,
|
||||
"y": 40,
|
||||
"width": 240,
|
||||
"height": 48,
|
||||
"text": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"runs": [{"text": "方案"}]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
# 钉钉白板常用 Recipes
|
||||
|
||||
以下文件都传给。执行任何远端写入前,必须先向用户展示影响并取得明确确认;确认后
|
||||
才可添加 `--yes`:
|
||||
|
||||
```bash
|
||||
dws whiteboard update \
|
||||
--node <DOC_NODE_ID> \
|
||||
--part-id <WHITEBOARD_PART_ID> \
|
||||
--source <FILE.json> \
|
||||
--yes \
|
||||
--format json
|
||||
```
|
||||
|
||||
每次更新后都要再次执行 `dws whiteboard query ... --format json` 回读验证。
|
||||
|
||||
## 1. 追加两个流程节点和一条箭头
|
||||
|
||||
```json
|
||||
{
|
||||
"overwrite": false,
|
||||
"source": {
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "start",
|
||||
"type": "shape",
|
||||
"x": 80,
|
||||
"y": 100,
|
||||
"width": 160,
|
||||
"height": 72,
|
||||
"geometry": "dml:roundRect",
|
||||
"text": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"horizontalAlign": "center",
|
||||
"runs": [{ "text": "读取需求", "marks": { "bold": true } }]
|
||||
}
|
||||
],
|
||||
"verticalAlign": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "finish",
|
||||
"type": "shape",
|
||||
"x": 360,
|
||||
"y": 100,
|
||||
"width": 160,
|
||||
"height": 72,
|
||||
"geometry": "dml:roundRect",
|
||||
"text": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"horizontalAlign": "center",
|
||||
"runs": [{ "text": "输出结果", "marks": { "bold": true } }]
|
||||
}
|
||||
],
|
||||
"verticalAlign": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "connector",
|
||||
"start": {
|
||||
"type": "node",
|
||||
"nodeRef": { "scope": "request", "id": "start" },
|
||||
"anchor": { "mode": "fixed", "side": "right" }
|
||||
},
|
||||
"end": {
|
||||
"type": "node",
|
||||
"nodeRef": { "scope": "request", "id": "finish" },
|
||||
"anchor": { "mode": "fixed", "side": "left" },
|
||||
"marker": { "catalogId": "arrow.filled" }
|
||||
},
|
||||
"routing": "straight"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 追加带渐变和阴影的卡片
|
||||
|
||||
```json
|
||||
{
|
||||
"overwrite": false,
|
||||
"source": {
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "styled-card",
|
||||
"type": "shape",
|
||||
"x": 80,
|
||||
"y": 260,
|
||||
"width": 260,
|
||||
"height": 120,
|
||||
"geometry": "dml:roundRect",
|
||||
"style": {
|
||||
"fill": {
|
||||
"type": "linearGradient",
|
||||
"angle": 35,
|
||||
"stops": [
|
||||
{ "offset": 0, "color": "#DBEAFE" },
|
||||
{ "offset": 100, "color": "#A7F3D0" }
|
||||
]
|
||||
},
|
||||
"stroke": {
|
||||
"paint": { "type": "solid", "color": "#2563EB" },
|
||||
"width": 2
|
||||
},
|
||||
"effects": [
|
||||
{
|
||||
"type": "shadow",
|
||||
"offsetX": 5,
|
||||
"offsetY": 7,
|
||||
"blur": 18,
|
||||
"color": "#0F172A",
|
||||
"opacity": 0.22
|
||||
}
|
||||
]
|
||||
},
|
||||
"text": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"horizontalAlign": "center",
|
||||
"runs": [
|
||||
{
|
||||
"text": "复杂样式",
|
||||
"marks": { "fontSize": 20, "bold": true, "color": "#0F172A" }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"verticalAlign": "center"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Frame 中放置分支流程
|
||||
|
||||
先创建 frame,再让子节点通过 `parentId` 引用 frame 的临时 ID。子节点坐标相对
|
||||
frame 左上角:
|
||||
|
||||
```json
|
||||
{
|
||||
"overwrite": false,
|
||||
"source": {
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "pipeline",
|
||||
"type": "frame",
|
||||
"x": 60,
|
||||
"y": 440,
|
||||
"width": 720,
|
||||
"height": 300,
|
||||
"title": {
|
||||
"text": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"runs": [{ "text": "生成流水线", "marks": { "bold": true } }]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "branch-a",
|
||||
"type": "shape",
|
||||
"parentId": "pipeline",
|
||||
"x": 60,
|
||||
"y": 80,
|
||||
"width": 180,
|
||||
"height": 72,
|
||||
"geometry": "dml:rect"
|
||||
},
|
||||
{
|
||||
"id": "branch-b",
|
||||
"type": "shape",
|
||||
"parentId": "pipeline",
|
||||
"x": 420,
|
||||
"y": 80,
|
||||
"width": 180,
|
||||
"height": 72,
|
||||
"geometry": "dml:rect"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 上传 SVG 并追加 Vector
|
||||
|
||||
Vector 固定使用“上传 → 字段映射 → update → query”流程,并且所有命令使用同一个
|
||||
`DOC_NODE_ID`。
|
||||
|
||||
先上传 SVG。该命令只准备资源,不会插入文档正文:
|
||||
|
||||
```bash
|
||||
dws doc media upload \
|
||||
--node <DOC_NODE_ID> \
|
||||
--file ./icon.svg \
|
||||
--mime-type image/svg+xml \
|
||||
--yes \
|
||||
--format json
|
||||
```
|
||||
|
||||
从成功输出取 `resourceId` 和 `resourceUrl`:
|
||||
|
||||
```json
|
||||
{
|
||||
"nodeId": "<DOC_NODE_ID>",
|
||||
"resourceId": "resource-stable-id",
|
||||
"resourceUrl": "https://resource.example/resource-stable-id?resourceId=resource-stable-id",
|
||||
"fileName": "icon.svg",
|
||||
"mimeType": "image/svg+xml",
|
||||
"size": 1024
|
||||
}
|
||||
```
|
||||
|
||||
写入 `whiteboard-vector.json`,其中 `resourceId` 原样映射到
|
||||
`resource.resourceId`,`resourceUrl` 映射到 `resource.url`:
|
||||
|
||||
```json
|
||||
{
|
||||
"overwrite": false,
|
||||
"source": {
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "uploaded-vector",
|
||||
"type": "vector",
|
||||
"x": 80,
|
||||
"y": 80,
|
||||
"width": 160,
|
||||
"height": 160,
|
||||
"resource": {
|
||||
"kind": "managed",
|
||||
"resourceId": "resource-stable-id",
|
||||
"url": "https://resource.example/resource-stable-id?resourceId=resource-stable-id"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
执行更新:
|
||||
|
||||
```bash
|
||||
dws whiteboard update \
|
||||
--node <DOC_NODE_ID> \
|
||||
--part-id <WHITEBOARD_PART_ID> \
|
||||
--source ./whiteboard-vector.json \
|
||||
--yes \
|
||||
--format json
|
||||
```
|
||||
|
||||
最后独立回读,不以 update 的成功响应替代验证:
|
||||
|
||||
```bash
|
||||
dws whiteboard query \
|
||||
--node <DOC_NODE_ID> \
|
||||
--part-id <WHITEBOARD_PART_ID> \
|
||||
--format json
|
||||
```
|
||||
|
||||
禁止跨 nodeId 复用资源,也不要把本地 SVG 路径、独立文件节点 URL 或临时
|
||||
`uploadUrl` 写入 `resource.url`。
|
||||
|
||||
## 5. 整页替换或清空
|
||||
|
||||
把文件设为 `overwrite: true` 后,命令必须加 `--yes`:
|
||||
|
||||
```bash
|
||||
dws whiteboard update \
|
||||
--node <DOC_NODE_ID> \
|
||||
--part-id <WHITEBOARD_PART_ID> \
|
||||
--source ./overwrite.json \
|
||||
--yes \
|
||||
--format json
|
||||
```
|
||||
|
||||
清空整页:
|
||||
|
||||
```json
|
||||
{
|
||||
"overwrite": true,
|
||||
"source": {
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
仅当用户明确要求清空时使用。执行前必须 query、展示影响摘要并获得确认。
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
OpenNodes V1 的完整字段、节点类型、目录枚举和错误语义按需读取
|
||||
[协议索引](./whiteboard/open-nodes-v1.md);不要根据本页概要猜测节点字段或
|
||||
`geometry`、`catalogId` 等枚举值。
|
||||
`geometry`、`catalogId` 等枚举值。渐变卡片、Frame 分支、SVG/Vector 等完整
|
||||
工作流见 [常用 Recipes](./whiteboard/recipes.md)。
|
||||
|
||||
## 定位白板
|
||||
|
||||
@@ -37,7 +38,22 @@ CLI 会把服务端 `resultJson` 字符串解析为结构化 JSON。白板命令
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": [
|
||||
{"id":"title","type":"text","x":40,"y":40,"text":"方案"}
|
||||
{
|
||||
"id": "title",
|
||||
"type": "text",
|
||||
"x": 40,
|
||||
"y": 40,
|
||||
"width": 240,
|
||||
"height": 48,
|
||||
"text": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"runs": [{"text": "方案"}]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
# 钉钉白板常用 Recipes
|
||||
|
||||
以下文件都传给。执行任何远端写入前,必须先向用户展示影响并取得明确确认;确认后
|
||||
才可添加 `--yes`:
|
||||
|
||||
```bash
|
||||
dws whiteboard update \
|
||||
--node <DOC_NODE_ID> \
|
||||
--part-id <WHITEBOARD_PART_ID> \
|
||||
--source <FILE.json> \
|
||||
--yes \
|
||||
--format json
|
||||
```
|
||||
|
||||
每次更新后都要再次执行 `dws whiteboard query ... --format json` 回读验证。
|
||||
|
||||
## 1. 追加两个流程节点和一条箭头
|
||||
|
||||
```json
|
||||
{
|
||||
"overwrite": false,
|
||||
"source": {
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "start",
|
||||
"type": "shape",
|
||||
"x": 80,
|
||||
"y": 100,
|
||||
"width": 160,
|
||||
"height": 72,
|
||||
"geometry": "dml:roundRect",
|
||||
"text": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"horizontalAlign": "center",
|
||||
"runs": [{ "text": "读取需求", "marks": { "bold": true } }]
|
||||
}
|
||||
],
|
||||
"verticalAlign": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "finish",
|
||||
"type": "shape",
|
||||
"x": 360,
|
||||
"y": 100,
|
||||
"width": 160,
|
||||
"height": 72,
|
||||
"geometry": "dml:roundRect",
|
||||
"text": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"horizontalAlign": "center",
|
||||
"runs": [{ "text": "输出结果", "marks": { "bold": true } }]
|
||||
}
|
||||
],
|
||||
"verticalAlign": "center"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "connector",
|
||||
"start": {
|
||||
"type": "node",
|
||||
"nodeRef": { "scope": "request", "id": "start" },
|
||||
"anchor": { "mode": "fixed", "side": "right" }
|
||||
},
|
||||
"end": {
|
||||
"type": "node",
|
||||
"nodeRef": { "scope": "request", "id": "finish" },
|
||||
"anchor": { "mode": "fixed", "side": "left" },
|
||||
"marker": { "catalogId": "arrow.filled" }
|
||||
},
|
||||
"routing": "straight"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 追加带渐变和阴影的卡片
|
||||
|
||||
```json
|
||||
{
|
||||
"overwrite": false,
|
||||
"source": {
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "styled-card",
|
||||
"type": "shape",
|
||||
"x": 80,
|
||||
"y": 260,
|
||||
"width": 260,
|
||||
"height": 120,
|
||||
"geometry": "dml:roundRect",
|
||||
"style": {
|
||||
"fill": {
|
||||
"type": "linearGradient",
|
||||
"angle": 35,
|
||||
"stops": [
|
||||
{ "offset": 0, "color": "#DBEAFE" },
|
||||
{ "offset": 100, "color": "#A7F3D0" }
|
||||
]
|
||||
},
|
||||
"stroke": {
|
||||
"paint": { "type": "solid", "color": "#2563EB" },
|
||||
"width": 2
|
||||
},
|
||||
"effects": [
|
||||
{
|
||||
"type": "shadow",
|
||||
"offsetX": 5,
|
||||
"offsetY": 7,
|
||||
"blur": 18,
|
||||
"color": "#0F172A",
|
||||
"opacity": 0.22
|
||||
}
|
||||
]
|
||||
},
|
||||
"text": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"horizontalAlign": "center",
|
||||
"runs": [
|
||||
{
|
||||
"text": "复杂样式",
|
||||
"marks": { "fontSize": 20, "bold": true, "color": "#0F172A" }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"verticalAlign": "center"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Frame 中放置分支流程
|
||||
|
||||
先创建 frame,再让子节点通过 `parentId` 引用 frame 的临时 ID。子节点坐标相对
|
||||
frame 左上角:
|
||||
|
||||
```json
|
||||
{
|
||||
"overwrite": false,
|
||||
"source": {
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "pipeline",
|
||||
"type": "frame",
|
||||
"x": 60,
|
||||
"y": 440,
|
||||
"width": 720,
|
||||
"height": 300,
|
||||
"title": {
|
||||
"text": {
|
||||
"blocks": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"runs": [{ "text": "生成流水线", "marks": { "bold": true } }]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "branch-a",
|
||||
"type": "shape",
|
||||
"parentId": "pipeline",
|
||||
"x": 60,
|
||||
"y": 80,
|
||||
"width": 180,
|
||||
"height": 72,
|
||||
"geometry": "dml:rect"
|
||||
},
|
||||
{
|
||||
"id": "branch-b",
|
||||
"type": "shape",
|
||||
"parentId": "pipeline",
|
||||
"x": 420,
|
||||
"y": 80,
|
||||
"width": 180,
|
||||
"height": 72,
|
||||
"geometry": "dml:rect"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 上传 SVG 并追加 Vector
|
||||
|
||||
Vector 固定使用“上传 → 字段映射 → update → query”流程,并且所有命令使用同一个
|
||||
`DOC_NODE_ID`。
|
||||
|
||||
先上传 SVG。该命令只准备资源,不会插入文档正文:
|
||||
|
||||
```bash
|
||||
dws doc media upload \
|
||||
--node <DOC_NODE_ID> \
|
||||
--file ./icon.svg \
|
||||
--mime-type image/svg+xml \
|
||||
--yes \
|
||||
--format json
|
||||
```
|
||||
|
||||
从成功输出取 `resourceId` 和 `resourceUrl`:
|
||||
|
||||
```json
|
||||
{
|
||||
"nodeId": "<DOC_NODE_ID>",
|
||||
"resourceId": "resource-stable-id",
|
||||
"resourceUrl": "https://resource.example/resource-stable-id?resourceId=resource-stable-id",
|
||||
"fileName": "icon.svg",
|
||||
"mimeType": "image/svg+xml",
|
||||
"size": 1024
|
||||
}
|
||||
```
|
||||
|
||||
写入 `whiteboard-vector.json`,其中 `resourceId` 原样映射到
|
||||
`resource.resourceId`,`resourceUrl` 映射到 `resource.url`:
|
||||
|
||||
```json
|
||||
{
|
||||
"overwrite": false,
|
||||
"source": {
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "uploaded-vector",
|
||||
"type": "vector",
|
||||
"x": 80,
|
||||
"y": 80,
|
||||
"width": 160,
|
||||
"height": 160,
|
||||
"resource": {
|
||||
"kind": "managed",
|
||||
"resourceId": "resource-stable-id",
|
||||
"url": "https://resource.example/resource-stable-id?resourceId=resource-stable-id"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
执行更新:
|
||||
|
||||
```bash
|
||||
dws whiteboard update \
|
||||
--node <DOC_NODE_ID> \
|
||||
--part-id <WHITEBOARD_PART_ID> \
|
||||
--source ./whiteboard-vector.json \
|
||||
--yes \
|
||||
--format json
|
||||
```
|
||||
|
||||
最后独立回读,不以 update 的成功响应替代验证:
|
||||
|
||||
```bash
|
||||
dws whiteboard query \
|
||||
--node <DOC_NODE_ID> \
|
||||
--part-id <WHITEBOARD_PART_ID> \
|
||||
--format json
|
||||
```
|
||||
|
||||
禁止跨 nodeId 复用资源,也不要把本地 SVG 路径、独立文件节点 URL 或临时
|
||||
`uploadUrl` 写入 `resource.url`。
|
||||
|
||||
## 5. 整页替换或清空
|
||||
|
||||
把文件设为 `overwrite: true` 后,命令必须加 `--yes`:
|
||||
|
||||
```bash
|
||||
dws whiteboard update \
|
||||
--node <DOC_NODE_ID> \
|
||||
--part-id <WHITEBOARD_PART_ID> \
|
||||
--source ./overwrite.json \
|
||||
--yes \
|
||||
--format json
|
||||
```
|
||||
|
||||
清空整页:
|
||||
|
||||
```json
|
||||
{
|
||||
"overwrite": true,
|
||||
"source": {
|
||||
"schemaVersion": "1.0",
|
||||
"catalogVersion": "dml-v1",
|
||||
"nodes": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
仅当用户明确要求清空时使用。执行前必须 query、展示影响摘要并获得确认。
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
package unit_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestWhiteboardQuickExamplesUseWritableTextNodes(t *testing.T) {
|
||||
paths := []string{
|
||||
"../../skills/mono/references/products/whiteboard.md",
|
||||
"../../skills/multi/dingtalk-misc/references/whiteboard.md",
|
||||
}
|
||||
for _, path := range paths {
|
||||
path := path
|
||||
t.Run(filepath.Base(filepath.Dir(path))+"/"+filepath.Base(path), func(t *testing.T) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
payload := firstJSONFence(t, string(data))
|
||||
source, _ := payload["source"].(map[string]any)
|
||||
nodes, _ := source["nodes"].([]any)
|
||||
if len(nodes) == 0 {
|
||||
t.Fatalf("quick example has no source.nodes: %#v", payload)
|
||||
}
|
||||
node, _ := nodes[0].(map[string]any)
|
||||
if node["type"] != "text" {
|
||||
t.Fatalf("quick example first node type = %#v, want text", node["type"])
|
||||
}
|
||||
for _, dimension := range []string{"width", "height"} {
|
||||
value, ok := node[dimension].(float64)
|
||||
if !ok || value <= 0 {
|
||||
t.Errorf("quick example %s = %#v, want positive number", dimension, node[dimension])
|
||||
}
|
||||
}
|
||||
text, ok := node["text"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("quick example text = %#v, want OpenNodes text object", node["text"])
|
||||
}
|
||||
blocks, _ := text["blocks"].([]any)
|
||||
if len(blocks) == 0 {
|
||||
t.Fatalf("quick example text.blocks = %#v, want non-empty array", text["blocks"])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhiteboardRecipesAreDeliveredToBothSkillSurfaces(t *testing.T) {
|
||||
monoPath := "../../skills/mono/references/products/whiteboard/recipes.md"
|
||||
multiPath := "../../skills/multi/dingtalk-misc/references/whiteboard/recipes.md"
|
||||
mono, err := os.ReadFile(monoPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
multi, err := os.ReadFile(multiPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(mono) != string(multi) {
|
||||
t.Fatal("mono and multi whiteboard recipes differ")
|
||||
}
|
||||
content := string(mono)
|
||||
for _, required := range []string{
|
||||
"## 1. 追加两个流程节点和一条箭头",
|
||||
"## 2. 追加带渐变和阴影的卡片",
|
||||
"## 3. Frame 中放置分支流程",
|
||||
"## 4. 上传 SVG 并追加 Vector",
|
||||
"## 5. 整页替换或清空",
|
||||
"执行任何远端写入前,必须先向用户展示影响并取得明确确认",
|
||||
`"resourceUrl": "https://resource.example/resource-stable-id?resourceId=resource-stable-id"`,
|
||||
`"url": "https://resource.example/resource-stable-id?resourceId=resource-stable-id"`,
|
||||
} {
|
||||
if !strings.Contains(content, required) {
|
||||
t.Errorf("whiteboard recipes missing %q", required)
|
||||
}
|
||||
}
|
||||
for _, fence := range bashFences(content) {
|
||||
if strings.Contains(fence, "dws whiteboard update") || strings.Contains(fence, "dws doc media upload") {
|
||||
if !strings.Contains(fence, "--yes") {
|
||||
t.Errorf("whiteboard write example lacks --yes:\n%s", fence)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func bashFences(markdown string) []string {
|
||||
const marker = "```bash\n"
|
||||
var fences []string
|
||||
for {
|
||||
start := strings.Index(markdown, marker)
|
||||
if start < 0 {
|
||||
return fences
|
||||
}
|
||||
markdown = markdown[start+len(marker):]
|
||||
end := strings.Index(markdown, "\n```")
|
||||
if end < 0 {
|
||||
return fences
|
||||
}
|
||||
fences = append(fences, markdown[:end])
|
||||
markdown = markdown[end+len("\n```"):]
|
||||
}
|
||||
}
|
||||
|
||||
func firstJSONFence(t *testing.T, markdown string) map[string]any {
|
||||
t.Helper()
|
||||
const marker = "```json\n"
|
||||
start := strings.Index(markdown, marker)
|
||||
if start < 0 {
|
||||
t.Fatal("markdown has no JSON fence")
|
||||
}
|
||||
start += len(marker)
|
||||
end := strings.Index(markdown[start:], "\n```")
|
||||
if end < 0 {
|
||||
t.Fatal("JSON fence is not closed")
|
||||
}
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal([]byte(markdown[start:start+end]), &payload); err != nil {
|
||||
t.Fatalf("decode first JSON fence: %v", err)
|
||||
}
|
||||
return payload
|
||||
}
|
||||
Reference in New Issue
Block a user