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

11 KiB
Raw Blame History

OpenNodes V1 — 支持矩阵、富文本和样式

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

7. 节点类型

7.1 支持矩阵

type query update 主要字段
shape 支持 支持 geometrytext?style?
text 支持 支持 textstyle?
connector 支持 支持 startendroutingwaypoints?style?
stickyNote 支持 支持 text?style?
frame 支持 支持 title?style?presentationOrder?resizeMode?
group 支持 支持 子关系通过 parentId 表达
image 支持 只读 仅公共字段
vector 支持 支持 resource
icon 支持 支持 catalogId
path 支持 支持 pathstyle?
pdf 支持 只读 仅公共字段
media 支持 只读 仅公共字段
webLink 支持 只读 仅公共字段
table 支持 只读 仅公共字段
chart 支持 只读 仅公共字段
uml 支持 只读 仅公共字段
swimlane 支持 只读 仅公共字段
mind 支持 只读 仅公共字段
timer 支持 只读 仅公共字段
placeholder 支持 只读 仅公共字段
unknown 支持 只读 未识别或尚未定义独立语义的节点统一映射到此类型

表中标记为只读的类型仍会完整返回公共几何、层级和顺序信息,但 update 提交会以 nodeTypeUnsupported 失败。

timertablewebLink 不支持 V1 update。query 仅返回这些节点的公共几何、 层级、顺序和诊断字段,不承诺完整业务字段。文字 run 中的 link 是富文本能力, 不属于 webLink 节点,V1 支持读写。

webLink 只保留只读查询;OpenNodes V1 不支持创建或重建该节点。

7.2 Text

query 和 update 均支持普通段落、无序列表、有序列表、多 block、多 run 和文字 链接:

interface OpenTextRun {
  text: string;
  marks?: {
    fontFamily?: string;
    fontSize?: number;
    bold?: boolean;
    italic?: boolean;
    underline?: boolean;
    strike?: boolean;
    color?: string;
    highlight?: string;
  };
  link?: { url: string };
}

interface OpenTextBlock {
  type: "paragraph" | "bulletList" | "orderedList";
  horizontalAlign?: "left" | "center" | "right";
  runs: OpenTextRun[];
}

interface OpenTextWrite {
  blocks: OpenTextBlock[];
  verticalAlign?: "top" | "center" | "bottom";
  padding?: number | [number, number];
}

interface OpenText extends OpenTextWrite {
  plainText: string;
  writeSupport: "readWrite" | "readOnly";
  unsupportedFeatures?: string[];
}

约束:

  • blocks.length >= 1,每个 block 的 type 必须是 paragraphbulletListorderedList
  • 每个 block 都必须满足 runs.length >= 1
  • run 的 text 不能包含 \r\n、U+2028 或 U+2029。换行和列表项使用 独立 block 表达;每一段或每个列表项应写成一个 block,而不是把原始换行符 放进单个 run。
  • 每个 bulletList / orderedList block 表示一个列表项;服务端会把连续且同类的 block 解释为同一个列表中的多个列表项。
  • link.url 长度必须为 1..2048,不能包含控制字符、<>。支持无 scheme 的相对/裸链接,以及 httphttpsmailtoteldingtalk scheme javascript:data: 等可执行或未知 scheme 会被拒绝。
  • 相邻且 URL 相同的 linked run 会呈现为同一个链接,同时保留各 run 自己的 marks。
  • fontSize > 0padding 各项必须大于等于 0
  • plainText、文本级 writeSupportunsupportedFeatures 是 query-only。
  • text 节点必须提供 textshape 和 stickyNote 的 text 可省略。
  • 受支持的 paragraph、列表、链接、多 run 都保持 writeSupport = "readWrite";未知 list style、非法链接、未支持的 block 或 run marks 会令文本和所属节点变为 readOnly

下面的文本会显示为两个段落,第一段由两个不同样式的 run 组成:

{
  "blocks": [
    {
      "type": "paragraph",
      "horizontalAlign": "left",
      "runs": [
        {
          "text": "OpenNodes ",
          "marks": { "fontSize": 18, "bold": true, "color": "#2563EB" }
        },
        {
          "text": "rich text",
          "marks": { "fontSize": 18, "italic": true, "color": "#0F172A" }
        }
      ]
    },
    {
      "type": "paragraph",
      "horizontalAlign": "right",
      "runs": [
        {
          "text": "第二段",
          "marks": { "fontSize": 16, "underline": true, "color": "#047857" }
        }
      ]
    }
  ],
  "verticalAlign": "center",
  "padding": [4, 8]
}

同一 OpenTextWrite 结构适用于独立 text 节点、shape 文本、stickyNote 文本和 frame title。

下面三个 block 会生成两个无序列表项,其中第二项包含文字链接:

{
  "blocks": [
    {
      "type": "bulletList",
      "runs": [{ "text": "准备输入数据" }]
    },
    {
      "type": "bulletList",
      "runs": [
        {
          "text": "查看钉钉文档",
          "marks": { "underline": true },
          "link": { "url": "https://alidocs.dingtalk.com" }
        }
      ]
    },
    {
      "type": "orderedList",
      "runs": [{ "text": "执行生成" }]
    }
  ]
}

颜色接受以下 CSS 形式:3/4/6/8 位十六进制、颜色名,以及 rgb()rgba()hsl()hsla()oklch()lab()lch()color()。字符串最长 128 字符,不能包含控制字符、<>;。 这里只接受可独立解析的字面量;依赖外部样式上下文的 var()calc()color-mix() 等动态表达式不属于 V1。颜色名必须是标准 CSS named color, 任意字母串不会被当成颜色。

7.3 Style

query 可表达:

  • nonesolidtheme、线性渐变、径向渐变和图片 paint。
  • shadow、blur 和 unknown effect。

V1 update 支持 nonesolidtheme、线性渐变、九方向径向渐变,以及 单个自定义 shadow。主题明暗参数和渐变色标位置使用 0100 的百分比, 不会归一化成 01

type OpenRadialGradientPosition =
  | "topLeft"
  | "topCenter"
  | "topRight"
  | "centerLeft"
  | "center"
  | "centerRight"
  | "bottomLeft"
  | "bottomCenter"
  | "bottomRight";

interface OpenColorStop {
  offset: number;
  color: string;
  opacity?: number;
}

interface OpenImagePaint {
  type: "image";
  resource: {
    kind: "managed" | "external" | "embedded" | "unresolved";
    resourceId?: string;
  };
  intrinsicWidth: number;
  intrinsicHeight: number;
}

type OpenPaint =
  | { type: "none" }
  | { type: "solid"; color: string; opacity?: number }
  | {
      type: "theme";
      token: string;
      lumMod?: number;
      lumOff?: number;
      resolvedColor?: string;
    }
  | {
      type: "linearGradient";
      angle: number;
      stops: OpenColorStop[];
    }
  | {
      type: "radialGradient";
      position: OpenRadialGradientPosition | "custom";
      stops: OpenColorStop[];
    }
  | OpenImagePaint;

type OpenEffect =
  | {
      type: "shadow";
      offsetX: number;
      offsetY: number;
      blur: number;
      color: string;
      opacity: number;
    }
  | { type: "blur"; blur: number }
  | { type: "unknown" };

interface OpenNodeStyle {
  opacity?: number;
  fill?: OpenPaint;
  stroke?: {
    paint: OpenPaint;
    width?: number;
    dash?: number[];
    lineCap?: "butt" | "round" | "square";
    lineJoin?: "miter" | "round" | "bevel";
  };
  effects?: OpenEffect[];
  writeSupport: "readWrite" | "readOnly";
  unsupportedFeatures?: string[];
}

interface OpenColorStopWrite {
  offset: number; // [0, 100],百分比
  color: string;
  opacity?: number; // [0, 1]
}

type OpenPaintWrite =
  | { type: "none" }
  | { type: "solid"; color: string; opacity?: number }
  | {
      type: "theme";
      token: string;
      lumMod?: number; // [0, 100],默认 100
      lumOff?: number; // [0, 100],默认 0
    }
  | {
      type: "linearGradient";
      angle: number;
      stops: OpenColorStopWrite[];
    }
  | {
      type: "radialGradient";
      position: OpenRadialGradientPosition;
      stops: OpenColorStopWrite[];
    };

interface OpenShadowEffectWrite {
  type: "shadow";
  offsetX: number;
  offsetY: number;
  blur: number;
  color: string;
  opacity: number;
}

interface OpenNodeStyleWrite {
  opacity?: number;
  fill?: OpenPaintWrite;
  stroke?: {
    paint: OpenPaintWrite;
    width?: number;
    dash?: number[];
    lineCap?: "butt" | "round" | "square";
    lineJoin?: "miter" | "round" | "bevel";
  };
  effects?: OpenShadowEffectWrite[];
}

约束:

  • opacity 范围为 [0, 1]
  • solid paint 的 coloropacity 在 query 后仍保持独立字段,不会合并为 动态 CSS 表达式。
  • theme 的 token 必须能在当前白板主题中解析; lumModlumOff 范围均为 [0, 100]。token 不存在时在 Request graph 阶段返回 themeTokenNotFound,不会写出部分节点。 token 长度为 164,首尾不能有空白,也不能包含空白、控制字符、 <>;
  • query 的 theme paint 还会返回按当前白板主题计算出的 query-only resolvedColor。调用方写入时只传 token/lumMod/lumOff,不传 resolvedColor
  • 渐变必须至少有一个 stopoffset 范围为 [0, 100],单位是百分比。
  • 线性渐变 angle 范围为 [0, 360]
  • 径向渐变只接受上述九宫格位置。query 遇到九宫格以外的位置时返回 position: "custom" 并将该节点标为只读。
  • effects 最多包含一个 shadowoffsetX/offsetY 必须是有限数, blur >= 0shadow opacity 范围为 [0, 1]
  • stroke width 和 dash 各项必须大于等于 0
  • 一旦提供 strokestroke.paint 必填。
  • 样式级 writeSupportunsupportedFeatures 是 query-only。
  • 能在当前白板主题中解析且明暗参数合法的 theme paint 可写。无法解析的主题 token、越界的主题明暗参数、image paint、blur、unknown effect、叠加 effect 或自定义径向位置会令节点只读;受支持的 theme、渐变和单个 shadow 本身不会 令节点只读。

主题色示例:

{
  "fill": {
    "type": "theme",
    "token": "ac3",
    "lumMod": 20,
    "lumOff": 80
  },
  "stroke": {
    "paint": {
      "type": "theme",
      "token": "sk1",
      "lumMod": 80,
      "lumOff": 20
    },
    "width": 2
  }
}

示例中的 ac3sk1 只是主题 token 示例,不是所有白板都可用的全局枚举。 调用方只能使用已确认可由当前白板主题解析的 token;无法确认时应改用 solid 颜色。

DWS 不提供新增、修改或切换主题的命令。当前白板没有有效主题时,应改用 solid

示例:

{
  "fill": {
    "type": "linearGradient",
    "angle": 35,
    "stops": [
      { "offset": 0, "color": "#1677ff", "opacity": 0.4 },
      { "offset": 100, "color": "#69b1ff" }
    ]
  },
  "effects": [
    {
      "type": "shadow",
      "offsetX": 8,
      "offsetY": 8,
      "blur": 19,
      "color": "rgba(93,190,172,1)",
      "opacity": 0.5
    }
  ]
}

未传 style 时使用节点类型的默认样式。调用方如需稳定的视觉结果,应显式传入 fillstroke