Files
EP-Hub-Skill/.agents/skills/dingtalk-misc/references/whiteboard/open-nodes-v1/06-vector-icon-path.md
T
2026-09-02 11:44:52 +08:00

9.4 KiB
Raw Blame History

OpenNodes V1 — Vector、Icon 和 Path

本文件是 DWS OpenNodes V1 协议的拆分章节。按需读取入口见 协议索引

7.9 Vector(已上传 SVG/矢量资源)

vector 表示已经通过 dws doc media upload 获得稳定引用的 SVG/矢量图片。 OpenNodes 只接收上传结果中的资源引用,不接收本地路径或原始 SVG/XML 内容。

上传时必须使用与后续白板更新相同的文档 nodeId

dws doc media upload \
  --node <DOC_NODE_ID> \
  --file ./icon.svg \
  --mime-type image/svg+xml \
  --format json

将上传结果的 resourceIdresourceUrl 分别写入 resource.resourceIdresource.url

Update 必须提供完整的托管资源信息:

interface OpenManagedVectorResourceWrite {
  kind: "managed";
  resourceId: string;
  url: string;
}

完整节点结构见第 6 节 OpenVectorNodeWrite

resource 字段规则:

字段 必填 规则
kind 当前只允许固定值 managed,表示资源已经通过 DWS 上传。
resourceId 资源稳定 ID,长度 1~256,只允许字母、数字、._:-
url 已上传资源地址,最长 4096;必须包含且只能包含一个同值的 resourceId 查询参数。

url 必须直接使用 dws doc media upload 返回的 resourceUrl,不得自行拼装 或修改。

以下内容会被拒绝:

  • 原始 SVG/XML、data:blob:http: URL。
  • //host/path 协议相对地址,以及自行构造或修改的其他相对地址。
  • 含空白、控制字符、反斜杠、fragment 或用户凭证的 URL。
  • URL 缺少 resourceId、重复出现 resourceId,或者 URL 中 ID 与显式 resource.resourceId 不一致。
  • resource 缺失、字段不完整、kind 不是 managed,或包含未知字段。

完整 Append 示例:

{
  "source": {
    "schemaVersion": "1.0",
    "catalogVersion": "dml-v1",
    "nodes": [
      {
        "id": "uploaded-svg-1",
        "type": "vector",
        "x": 100,
        "y": 80,
        "width": 240,
        "height": 180,
        "angle": 0,
        "resource": {
          "kind": "managed",
          "resourceId": "0c1c94e1-f9af-4228-b32f-42bbd1555253",
          "url": "https://resources.example.com/assets/opaque-path?resourceId=0c1c94e1-f9af-4228-b32f-42bbd1555253"
        }
      }
    ]
  },
  "overwrite": false
}

示例中的 resources.example.com 是占位域名,实际调用必须使用 dws doc media upload 返回的 resourceUrl

resourceId 同时显式出现并包含在 URL 中,用于校验资源身份与地址是否一致。 两者必须来自同一次 dws doc media upload 结果。

Query 的 resource 可能是:

type OpenVectorResource =
  | { kind: "managed"; resourceId: string; url: string }
  | { kind: "external" | "embedded" | "unresolved" };
  • 能识别为托管资源的引用返回完整 managed 信息。
  • HTTP(S) 外链但不满足托管资源契约时返回 external
  • data:/blob: 返回 embedded
  • 其他缺失或无法识别的地址返回 unresolved
  • managed 资源会令节点只读,并分别产生 vector.resource.externalvector.resource.embeddedvector.resource.unresolved

V1 vector update 不接受 style。既有节点包含 V1 无法表达的 fill、stroke、 opacity、effect 或 adjustments 时,query 仍可读取资源和几何,但节点会标为只读。 不影响资源内容的兼容性装饰不会单独令节点变为只读。

DWS 会校验资源引用。上传与 whiteboard update 必须使用同一个文档 nodeId;不要跨文档复用资源,也不要使用临时 uploadUrl

7.10 Icon(内置图标)

icon 表示内置图标。OpenNodes 使用版本化的 catalogId 作为稳定标识, 允许值见下列类型定义和附录 B。

Update 结构:

type OpenIconCatalogId =
  | `emoji/${
      | "happy"
      | "smile"
      | "laugh"
      | "fighting"
      | "like"
      | "ok"
      | "please"
      | "face-plam"
      | "tears-of-joy"
      | "cry"
      | "question"
      | "face-with-sweat"
      | "bloody-nose"
      | "doggy"}`
  | `tools/${
      | "pad"
      | "blue-note"
      | "yellow-notes"
      | "chart"
      | "chart-2"
      | "pencil"
      | "pen"
      | "bag"
      | "rocket"
      | "fire"
      | "gold"
      | "light"
      | "pin"
      | "red-flag"
      | "tea"
      | "island"
      | "ball"
      | "lucky-fish"
      | "coffee"
      | "milky-tea"
      | "pan"}`
  | `priority/priority-${1 | 2 | 3 | 4 | 5 | 6 | 7}`
  | `task/${
      | "task-start"
      | "task-oct"
      | "task-3oct"
      | "task-half"
      | "task-5oct"
      | "task-7oct"
      | "task-done"}`;

完整节点结构见第 6 节 OpenIconNodeWrite

完整 Append 示例:

{
  "source": {
    "schemaVersion": "1.0",
    "catalogVersion": "dml-v1",
    "nodes": [
      {
        "id": "pencil-icon",
        "type": "icon",
        "x": 100,
        "y": 80,
        "width": 48,
        "height": 48,
        "catalogId": "tools/pencil"
      }
    ]
  },
  "overwrite": false
}

规则:

  • catalogId 必填,格式为 <group>/<name>,并且必须精确命中附录 B 的 dml-v1 allowlist;大小写、连字符和历史拼写都不能自行修正。
  • 当前目录有 emojitoolsprioritytask 四组,共 49 项。
  • update 只接受 catalogId,不接受 groupnamestyleresource
  • 内置 icon 不需要调用方上传资源,也不需要 resourceId
  • 任意已上传 SVG 或自定义图标应使用 vector,并按 7.9 节提供完整 resource 信息,不能伪造一个 icon catalogId
  • icon 可以作为 group/frame 子节点;V1 connector 的 node 端点当前仍不接受 icon 作为目标。

Query 返回相同的 catalogId。既有 icon 无法映射到当前目录时,节点仍会以 type: "icon" 返回以便诊断,但 writeSupportreadOnly,原因包含 icon.catalogId。非默认 opacity、filter/effect、text 或 adjustments 同样会令节点 只读。

7.11 Path(自由画笔)

path 表示自由画笔轨迹。OpenNodes 保留两组互相独立的尺寸:

  • 节点公共 width / height 是画布上的实际渲染尺寸,缩放节点时会变化。
  • path.intrinsicWidth / path.intrinsicHeight 是 SVG path 自身的坐标空间尺寸, 表示 path.data 使用的内部坐标空间。
interface OpenPathData {
  data: string;
  intrinsicWidth: number;
  intrinsicHeight: number;
}

type OpenPathDataWrite = OpenPathData;

完整 update 节点结构见第 6 节 OpenPathNodeWrite。query 和 update 的 path 字段结构相同,但 update 仍必须满足下方命令子集和大小限制。

{
  "id": "freehand-stroke",
  "type": "path",
  "x": 120,
  "y": 100,
  "width": 500,
  "height": 150,
  "path": {
    "data": "M0,75 Q50,0 100,75 Q150,150 200,75 Q250,0 300,75 Q350,150 400,75 Q450,0 500,75",
    "intrinsicWidth": 500,
    "intrinsicHeight": 150
  },
  "style": {
    "fill": { "type": "none" },
    "stroke": {
      "paint": { "type": "solid", "color": "#7C3AED" },
      "width": 10,
      "lineCap": "round",
      "lineJoin": "round"
    }
  }
}

下面是一个仍然只使用 V1 命令子集、但包含 12 段二次贝塞尔曲线的蝴蝶轮廓。 它显式回到起点,因此不需要使用尚未支持的 Z

{
  "id": "complex-butterfly-path",
  "type": "path",
  "x": 1500,
  "y": 1215,
  "width": 500,
  "height": 420,
  "path": {
    "data": "M250,180 Q210,105 145,70 Q55,25 35,110 Q10,195 125,220 Q35,275 80,355 Q125,415 210,315 Q235,285 250,250 Q265,285 290,315 Q375,415 420,355 Q465,275 375,220 Q490,195 465,110 Q445,25 355,70 Q290,105 250,180",
    "intrinsicWidth": 500,
    "intrinsicHeight": 420
  },
  "style": {
    "opacity": 0.96,
    "fill": {
      "type": "solid",
      "color": "#EDE9FE",
      "opacity": 0.72
    },
    "stroke": {
      "paint": {
        "type": "solid",
        "color": "#6D28D9"
      },
      "width": 8,
      "lineCap": "round",
      "lineJoin": "round"
    }
  }
}

V1 path 写入约束如下:

  • data 必须是一个绝对 M,后跟至少一个显式写出的绝对 Q;不接受相对命令, 也不接受 LCAZ 等通用 SVG 命令。
  • 所有命令参数必须完整且为有限数值;单节点 data 最长 1 MiB,最多 50,000 个 命令。
  • intrinsicWidthintrinsicHeight 必须为有限正数;它们不要求等于节点的 widthheight
  • 未传 style 时,默认使用透明填充、#222222 描边、宽度 5,以及 round line cap/join。需要稳定视觉结果时仍应显式传入 style
  • path 可以作为 group/frame 的子节点,也可以作为同一 update 请求中 connector 的 node 端点。

query 会把其他能够解析的 SVG path 保留为 type: "path",但标记为 readOnly,原因包含 path.commands;超出上述 V1 限制时使用 path.data.sizepath.commands.limit。既有 path 带 text、adjustments、非 nonzero fillRule 或 V1 无法表达的样式时也会只读。仅包含不影响画笔几何的 兼容性信息时,不会因此变为只读。theme stroke 遵循通用 style 契约:query 会 保留 token 和明暗参数;只要 token 能在当前白板主题中解析,就可以按 theme paint 回写。