flowsvg 0.1.0 → 0.1.1

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/PATTERNS.md +12 -1
  3. package/README.md +139 -117
  4. package/README.zh-CN.md +139 -117
  5. package/dist/adapters/react.d.ts +48 -0
  6. package/dist/adapters/react.js +126 -0
  7. package/dist/adapters/vue.d.ts +132 -0
  8. package/dist/adapters/vue.js +161 -0
  9. package/dist/adapters/web-component.d.ts +99 -0
  10. package/dist/adapters/web-component.js +396 -0
  11. package/dist/browser/player.d.ts +69 -0
  12. package/dist/browser/player.js +353 -0
  13. package/dist/browser.d.ts +4 -69
  14. package/dist/browser.js +2 -353
  15. package/dist/core/color.d.ts +3 -0
  16. package/dist/core/color.js +18 -0
  17. package/dist/core/diagram.d.ts +38 -0
  18. package/dist/core/diagram.js +391 -0
  19. package/dist/core/edge-geometry.d.ts +32 -0
  20. package/dist/core/edge-geometry.js +382 -0
  21. package/dist/{core.d.ts → core/model.d.ts} +2 -52
  22. package/dist/core/model.js +1 -0
  23. package/dist/{node-content-paint.d.ts → core/node-content-paint.d.ts} +2 -2
  24. package/dist/{node-content-paint.js → core/node-content-paint.js} +11 -20
  25. package/dist/{node-content.d.ts → core/node-content.d.ts} +1 -1
  26. package/dist/core/svg-number.d.ts +5 -0
  27. package/dist/core/svg-number.js +7 -0
  28. package/dist/{theme.d.ts → core/theme.d.ts} +6 -0
  29. package/dist/core/theme.js +17 -0
  30. package/dist/{timeline.d.ts → core/timeline.d.ts} +1 -3
  31. package/dist/{timeline.js → core/timeline.js} +1 -17
  32. package/dist/core/validation.d.ts +11 -0
  33. package/dist/core/validation.js +389 -0
  34. package/dist/index.d.ts +5 -1
  35. package/dist/index.js +2 -1
  36. package/dist/react.d.ts +3 -48
  37. package/dist/react.js +2 -126
  38. package/dist/vue.d.ts +3 -132
  39. package/dist/vue.js +2 -161
  40. package/dist/web-component.d.ts +3 -103
  41. package/dist/web-component.js +2 -396
  42. package/docs/images/entity-graph.png +0 -0
  43. package/docs/images/facts-timeline.png +0 -0
  44. package/docs/images/facts-timeline.zh-CN.png +0 -0
  45. package/docs/images/node-content.png +0 -0
  46. package/docs/images/numbered-list.png +0 -0
  47. package/docs/images/numbered-list.zh-CN.png +0 -0
  48. package/docs/images/quick-start.png +0 -0
  49. package/docs/images/record-tags.png +0 -0
  50. package/package.json +2 -1
  51. package/dist/core.js +0 -1149
  52. package/dist/theme.js +0 -6
  53. /package/dist/{player-accessibility.d.ts → browser/accessibility.d.ts} +0 -0
  54. /package/dist/{player-accessibility.js → browser/accessibility.js} +0 -0
  55. /package/dist/{player-controls.d.ts → browser/controls.d.ts} +0 -0
  56. /package/dist/{player-controls.js → browser/controls.js} +0 -0
  57. /package/dist/{player-lifecycle.d.ts → browser/lifecycle.d.ts} +0 -0
  58. /package/dist/{player-lifecycle.js → browser/lifecycle.js} +0 -0
  59. /package/dist/{svg-dom.d.ts → browser/svg-dom.d.ts} +0 -0
  60. /package/dist/{svg-dom.js → browser/svg-dom.js} +0 -0
  61. /package/dist/{node-content.js → core/node-content.js} +0 -0
package/README.zh-CN.md CHANGED
@@ -1,12 +1,12 @@
1
- # flowsvg
1
+ # FlowSVG
2
2
 
3
3
  [English](README.md) | 简体中文
4
4
 
5
- flowsvg(流形)是 TypeScript ESM 声明式 SVG 图解播放器 SDK:用 JSON 或 TypeScript 对象描述逻辑图、架构图的节点、连线、场景与节拍,在网页中播放,或在无 DOM 环境导出指定时刻的 SVG。
5
+ FlowSVG(流形)用 SVG 播放逻辑图和架构图。用 JSON 或 TypeScript 定义节点、连线、场景与节拍,即可在浏览器播放或导出静态 SVG。
6
6
 
7
- 核心负责校验、时间求值、几何和 SVG;浏览器与框架接入负责播放、交互和生命周期。同一文档、时间和视觉选项得到一致的核心画面。节点悬停额外提供临时的浏览器反馈。
7
+ SDK 使用 ESM,提供无 DOM 核心及原生浏览器、Vue、React、Web Component 接入。各接入方式共用同一文档和时间线。
8
8
 
9
- [安装](#安装) · [快速开始](#快速开始) · [Vue](#vue-集成) · [React](#react-集成) · [原生浏览器](#原生浏览器) · [Web Component](#web-component) · [API](#api) · [开发](#开发与验证) · [License](#license)
9
+ [安装](#安装) · [快速开始](#快速开始) · [Vue](#vue-集成) · [React](#react-集成) · [原生浏览器](#原生浏览器) · [Web Component](#web-component) · [API](#api) · [License](#license)
10
10
 
11
11
  ## 安装
12
12
 
@@ -27,16 +27,7 @@ pnpm add react react-dom
27
27
  pnpm add -D @types/react @types/react-dom
28
28
  ```
29
29
 
30
- 核心与原生浏览器入口无需安装框架。发布前可在源码仓库根目录构建本地 tarball:
31
-
32
- ```sh
33
- pnpm install --frozen-lockfile
34
- pnpm --filter flowsvg exec npm pack --pack-destination /tmp
35
- ```
36
-
37
- 打包钩子会构建 SDK。生成的 `0.1.0` tarball 可通过 `pnpm add /tmp/flowsvg-0.1.0.tgz` 安装。
38
-
39
- 运行核心需要 Node.js 18+ 或支持 ES2022 ESM 的环境;浏览器播放器需要 DOM、SVG 与 `requestAnimationFrame`。框架最低版本为 Vue 3.5、React 18。TypeScript 类型随包提供,React TypeScript 应用还需安装对应的 `@types/react` 和 `@types/react-dom`。
30
+ 核心与原生浏览器入口无需框架,类型声明随包提供。核心要求 Node.js 18+ 或 ES2022 ESM;浏览器要求 DOM、SVG 和 `requestAnimationFrame`;框架最低版本为 Vue 3.5、React 18。
40
31
 
41
32
  | 导入路径 | 用途 |
42
33
  | --- | --- |
@@ -51,31 +42,63 @@ pnpm --filter flowsvg exec npm pack --pack-destination /tmp
51
42
 
52
43
  ### 1. 创建图解文档
53
44
 
54
- 将下面内容保存为 `diagram.ts`,后续集成示例复用此文件:
45
+ 将下面内容保存为 `diagram.ts`。这个 5.2 秒的示例包含六个节点、三个场景和五个节拍,依次展示身份校验、订单写库,以及异步事件与响应的并行传递。后续集成示例复用此文件:
55
46
 
56
47
  ```ts
57
48
  import type { DiagramDocument } from "flowsvg";
58
49
 
59
50
  export const diagramDocument: DiagramDocument = {
60
51
  version: 1,
61
- title: "请求链路",
62
- viewport: { width: 640, height: 280 },
52
+ title: "订单请求生命周期",
53
+ viewport: { width: 960, height: 440 },
63
54
  nodes: [
64
- { id: "client", kind: "roundedRect", x: 40, y: 100, width: 140, height: 80, label: "Client" },
65
- { id: "service", kind: "rect", x: 420, y: 100, width: 160, height: 80, label: "Service", visible: false }
55
+ { id: "client", kind: "roundedRect", x: 40, y: 205, width: 120, height: 70, label: "Client" },
56
+ { id: "gateway", kind: "roundedRect", x: 260, y: 205, width: 130, height: 70, label: "API gateway" },
57
+ { id: "auth", kind: "ellipse", x: 260, y: 40, width: 130, height: 72, label: "Auth" },
58
+ { id: "orders", kind: "roundedRect", x: 500, y: 205, width: 150, height: 70, label: "Order service" },
59
+ { id: "database", kind: "cylinder", x: 780, y: 65, width: 140, height: 90, label: "Orders" },
60
+ { id: "events", kind: "rect", x: 780, y: 330, width: 140, height: 70, label: "Event queue" }
66
61
  ],
67
- edges: [{ id: "request", from: "client", to: "service", label: "HTTP", route: "straight" }],
68
- scenes: [{
69
- id: "request-flow",
70
- label: "请求进入服务",
71
- beats: [
72
- { durationMs: 1000, transitionMs: 250, caption: "服务出现", actions: [{ type: "show", target: "service" }] },
73
- { durationMs: 1000, caption: "服务处理请求", actions: [{ type: "highlight", target: "service" }] }
74
- ]
75
- }]
62
+ edges: [
63
+ { id: "request", from: "client", to: "gateway", route: "straight" },
64
+ { id: "authorize", from: "gateway", to: "auth", route: "straight", fromPort: "top", toPort: "bottom" },
65
+ { id: "create", from: "gateway", to: "orders", route: "straight" },
66
+ { id: "persist", from: "orders", to: "database", route: "orthogonal", fromPort: "right", toPort: "left" },
67
+ { id: "publish", from: "orders", to: "events", route: "curved", fromPort: "bottom", toPort: "left" },
68
+ { id: "result", from: "orders", to: "gateway", route: "curved", fromPort: "bottom", toPort: "bottom" }
69
+ ],
70
+ scenes: [
71
+ { id: "request", label: "接收与认证", beats: [
72
+ { durationMs: 800, caption: "接收订单请求", actions: [
73
+ { type: "flow", target: "request", direction: "forward", text: "POST" }
74
+ ] },
75
+ { durationMs: 800, caption: "校验访问令牌", actions: [
76
+ { type: "flow", target: "authorize", direction: "forward", text: "Token" }
77
+ ] }
78
+ ] },
79
+ { id: "create-order", label: "创建订单", beats: [
80
+ { durationMs: 1000, caption: "将已认证请求交给订单服务", actions: [
81
+ { type: "flow", target: "create", direction: "forward", text: "Create" }
82
+ ] },
83
+ { durationMs: 1000, caption: "将新订单写入数据库", actions: [
84
+ { type: "flow", target: "persist", direction: "forward", text: "INSERT" }
85
+ ] }
86
+ ] },
87
+ { id: "response", label: "发布事件与响应", beats: [
88
+ { durationMs: 1600, caption: "发布 OrderCreated 事件,同时返回订单号", actions: [
89
+ { type: "flow", target: "publish", direction: "forward", text: "OrderCreated" },
90
+ { type: "flow", target: "result", direction: "forward", text: "201 Created" },
91
+ { type: "flow", target: "request", direction: "reverse", text: "Order ID" }
92
+ ] }
93
+ ] }
94
+ ]
76
95
  };
77
96
  ```
78
97
 
98
+ 浅色主题下的 4400 ms 画面:事件与响应消息同时传递,仅高亮当前参与的节点。
99
+
100
+ ![订单流程包含 Client、API gateway、Auth、Order service、Orders 数据库和 Event queue,正在传递 OrderCreated 事件与响应消息](https://cdn.jsdelivr.net/npm/flowsvg@0.1.1/docs/images/quick-start.png)
101
+
79
102
  ### 2. 求值并导出 SVG
80
103
 
81
104
  ```ts
@@ -87,10 +110,10 @@ const frame = diagram.evaluateAt(125);
87
110
  console.log(diagram.totalDuration, frame.sceneId, frame.warnings);
88
111
 
89
112
  const svg = diagram.renderSvg(125, { theme: "dark" });
90
- const finalSvg = renderSvg(diagramDocument, 2000);
113
+ const finalSvg = renderSvg(diagramDocument, 5200);
91
114
  ```
92
115
 
93
- `svg` 和 `finalSvg` 是完整的静态 SVG 字符串,可以写入文件或用于预览。重复查询时复用 `createDiagram` 的结果;一次性导出可直接调用 `renderSvg`。网页播放可选择下方任一集成方式。
116
+ 两种调用均返回静态 SVG 字符串。重复查询时复用 `createDiagram`;单次导出用 `renderSvg`。
94
117
 
95
118
  ## Vue 集成
96
119
 
@@ -127,7 +150,7 @@ function handleError({ error }: PlayerErrorDetail): void {
127
150
  </template>
128
151
  ```
129
152
 
130
- 组件卸载时自动销毁播放器。脚本中通过 `player.value` 调用实例方法;模板会自动解包 ref。支持 `stepchange`、`timeupdate`、`playstatechange`、`error` 事件,参数直接为事件 detail;也可使用对应的回调 props。
153
+ 卸载时自动销毁。脚本中通过 `player.value` 调用方法,模板自动解包 ref。事件直接传入 detail:`stepchange`、`timeupdate`、`playstatechange`、`error`,也支持对应回调 props。
131
154
 
132
155
  ## React 集成
133
156
 
@@ -171,9 +194,9 @@ export default function DiagramExample() {
171
194
  | `initialTimeMs` | 仅初次挂载时应用;后续跳转用 `seek` |
172
195
  | `className` / `style` | 宿主容器样式 |
173
196
 
174
- 修改 `theme`、`controls`、`reducedMotion` 或 `hoverFill` 会重建播放器,暂停并回到零时刻;`initialTimeMs` 不会再次应用。修改文档时提供新的对象引用。ref / expose 提供下方播放器 API 中的七个控制和状态方法,清理由组件负责;挂载前或卸载后调用保留的 handle 会抛出未挂载错误。
197
+ 修改 `theme`、`controls`、`reducedMotion` 或 `hoverFill` 会重建播放器,暂停在零时刻,不再应用 `initialTimeMs`。更新文档需提供新对象引用。实例暴露[播放器 API](#播放器-api)中的七个方法;挂载前或卸载后调用会抛出错误。
175
198
 
176
- 两种框架入口可安全用于服务端导入;SSR 输出宿主容器,播放器在客户端挂载后创建。需要服务端图解预览时,使用核心的 `renderSvg`。
199
+ 两种入口均支持服务端导入。SSR 输出容器,客户端挂载后创建播放器。服务端图解预览使用 `renderSvg`。
177
200
 
178
201
  ## 原生浏览器
179
202
 
@@ -197,11 +220,9 @@ const player = createPlayer(host, diagramDocument, { theme: "light" });
197
220
  player.play();
198
221
  ```
199
222
 
200
- `createPlayer` 接管并替换容器子元素。在页面或组件卸载时调用 `player.destroy()`;在同一容器上创建新播放器前先销毁旧实例。原生 HTML 直接使用裸导入路径时需配置 import map,或通过应用构建工具处理;仓库的原生示例提供了 import map 配置。
223
+ `createPlayer` 替换容器子元素。卸载或复用容器前调用 `destroy()`。原生 HTML 使用包导入路径时,需配置 import map 或构建工具。
201
224
 
202
- 仓库中的 `apps/native-example/` 在固定镜头和布局下演示订单请求与响应。0–2400 ms,三条曲线分别显示圆点、`Token` 胶囊和反向预检胶囊;2400–4800 ms,另外两条曲线传递创建订单请求和库存确认;4800–7200 ms,响应消息返回网关和客户端。最终节拍在 8200 ms 结束,此时临时高亮已清除。相反方向的关系连线保持分离;悬停参与节点只增加浏览器高亮。
203
-
204
- 可将 `apps/native-example/request-chain.js` 作为原生、Web Component、React 或 Vue 的 `document` 输入。各宿主共用 `seek(timeMs)`、`setSpeed(speed)`、`play()` 和 `pause()` 行为。`src/business-integration.test.ts` 通过四种公开入口验证同一示例,覆盖两种主题、减少动画、节拍边界及核心 SVG 一致性。
225
+ [原生示例](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/README.md)演示请求与响应传递,同一文档也可用于其余三种接入方式。
205
226
 
206
227
  ## Web Component
207
228
 
@@ -221,13 +242,13 @@ const element = document.querySelector<FlowPlayerElement>("flowsvg-player");
221
242
  if (element) element.document = diagramDocument;
222
243
  ```
223
244
 
224
- 默认元素名为 `flowsvg-player`,重复注册安全。对象通过 `document` property 传入;HTML `document` attribute 接受 JSON 字符串,`src` 接受相对于页面地址解析的 JSON URL。URL 请求遵循浏览器的同源与 CORS 规则。
245
+ 默认注册 `<flowsvg-player>`,重复注册安全。对象赋给 `element.document`;JSON 字符串赋给 HTML `document` 属性;JSON URL 赋给 `src`。URL 相对页面解析,遵循浏览器 CORS 规则。
225
246
 
226
- 支持 `autoplay`、`controls`、`speed`、`theme`、`reduced-motion` 和 `hover-fill`。悬停背景默认关闭;设置 `hover-fill` 后同时提供背景和内容反馈。关闭默认显示的控件需使用 `controls="false"`;`autoplay` 默认关闭。选项变化时保留当前时间与播放/暂停状态。元素从 DOM 移除时清理播放器和请求,重新连接时可根据保留的文档重新挂载。
247
+ 支持 `autoplay`、`controls`、`speed`、`theme`、`reduced-motion`、`hover-fill`。默认显示控件,关闭自动播放与悬停背景;`controls="false"` 隐藏控件。主题、控件及动画/悬停选项变化时保留时间和播放状态;`autoplay` 则直接控制播放或暂停。移除元素会取消请求并销毁播放器,重新连接时使用保留的文档。
227
248
 
228
- 元素提供 `play`、`pause`、`seek`、`step`、`setSpeed`、`getState`、`destroy`;更换文档使用 `document` property。挂载前 `getState()` 返回 `undefined`。它将四类播放/错误事件以冒泡、可穿越 Shadow DOM 边界的 CustomEvent 转发。
249
+ 方法:`play`、`pause`、`seek`、`step`、`setSpeed`、`getState`、`destroy`。更换文档使用 `element.document`。挂载前或销毁后,`getState()` 返回 `undefined`。播放与错误事件可冒泡并穿越 Shadow DOM 边界。
229
250
 
230
- 实际注册需在 DOM 环境加载该入口。无 DOM 导入是安全的,但模块求值时会固定 HTMLElement 基类;同一个无 DOM 模块实例不能在稍后补上 DOM 后直接用于注册。
251
+ 请在 DOM 环境注册。无 DOM 导入安全,但该模块实例不能在稍后补上 DOM 后用于注册。
231
252
 
232
253
  ## 文档与时间线
233
254
 
@@ -240,17 +261,19 @@ if (element) element.document = diagramDocument;
240
261
  | `scenes` | `[]` 表示静态图;场景具有唯一 `id`、`label` 和非空 `beats` |
241
262
  | `title` / `theme` | 可选标题与 `light` / `dark` 文档主题 |
242
263
 
243
- 节点与连线的 ID 全局唯一。节点形状为 `rect`、`roundedRect`、`ellipse`、`cylinder`、`group`;`parentId` 引用父分组,子节点坐标相对分组原点。`label` 为纯文本,`visible` 默认 `true`。节点 `style` 支持 `fill`、`stroke`、`strokeWidth`、`textColor` 和 `dashed`(用于分组边框);连线 `style` 支持 `stroke`、`strokeWidth`、`dashed`。颜色使用十六进制 RGB/RGBA,例如 `#2563eb` 或 `#2563eb80`。
264
+ 节点与连线 ID 全局唯一。形状:`rect`、`roundedRect`、`ellipse`、`cylinder`、`group`。`parentId` 指向父分组,子节点坐标相对分组原点。标签为纯文本,`visible` 默认 `true`。节点样式:`fill`、`stroke`、`strokeWidth`、`textColor`、`dashed`(分组边框);连线样式:`stroke`、`strokeWidth`、`dashed`。颜色使用十六进制 RGB/RGBA,如 `#2563eb`、`#2563eb80`。
244
265
 
245
- 连线的 `route` 为 `straight`(默认)、`orthogonal` 或 `curved`。曲线根据端点方向端口生成平滑三次曲线,不会自动避让其他节点。正交路径避开可见普通节点,无法避让时使用固定回退;不保证最短路径或零交叉。需要固定连接侧时可使用 `fromPort` 和 `toPort`(`top`、`right`、`bottom` 或 `left`)。显式正交端口保留向外的端点线段,并绕过两端节点主体;曲线端口决定端点切线。省略端口时根据两节点中心的主方向自动选择,椭圆端口位于椭圆轮廓上。普通标签按确定性宽度估算换行,节点高度不足时保留完整标签并产生 `text-overflow` 警告。
266
+ 路由:`straight`(默认)、`orthogonal`、`curved`。曲线跟随端口切线,不避让其他节点;正交路径避开可见普通节点,受阻时固定回退,不保证最短路径或零交叉。`fromPort` / `toPort` 可设为 `top`、`right`、`bottom`、`left`。显式正交端口保留向外线段,曲线端口决定切线。省略时按中心连线主方向选择,椭圆端点位于轮廓。普通标签按估算宽度换行;高度溢出时仍显示全文,并报告 `text-overflow`。
246
267
 
247
- 连线可以连接普通节点、分组,或分别连接两者。分组端点使用当前求值后的分组矩形,跟随嵌套移动并支持显式端口。隐藏端点分组或其祖先时,连线同时隐藏。传递只高亮端点分组本身,子节点保留自身高亮状态。分组边框仍不作为布线障碍。
268
+ 连线可连接节点或分组边界。分组端点跟随当前几何和显式端口;祖先隐藏时遮蔽连线。传递仅高亮端点分组,不联动子节点。分组边框不作为布线障碍。
248
269
 
249
270
  ### 固定节点内的节点内容
250
271
 
251
- `rect`、`roundedRect` 和 `cylinder` 节点可用 `content` 替代 `label`。椭圆和分组仅接受普通标签。同时声明两个字段会校验失败,空标签也算声明。`content.blocks` 按顺序排列 `title`、`paragraph`、`list` 和 `graph` 内容块。段落 `role` 为 `body`(默认)或 `auxiliary`(弱化说明)。空内容块数组只显示形状。下例需从 `flowsvg` 导入 `DiagramNode` 类型:
272
+ `rect`、`roundedRect`、`cylinder` 支持 `content`;椭圆和分组仅支持普通标签。`content` 与 `label` 互斥,空标签也算声明。`content.blocks` 按顺序排列 `title`、`paragraph`、`list`、`graph` 内容块。段落角色为 `body`(默认)或 `auxiliary`(辅助文字);空数组只显示形状。
252
273
 
253
274
  ```ts
275
+ import type { DiagramNode } from "flowsvg";
276
+
254
277
  const service = {
255
278
  id: "service", kind: "rect", x: 24, y: 24, width: 260, height: 312,
256
279
  content: {
@@ -265,40 +288,55 @@ const service = {
265
288
  } satisfies DiagramNode;
266
289
  ```
267
290
 
268
- 标题/段落的 `text` 与列表条目的 `body`/`auxiliary`/`status`/文字标签 `text` 接受字符串或 `ContentTextSpan[]`。每个片段为 `{ text, style?: { fontSize, fontWeight, color } }`;对齐仍由内容块设置。例如 `text: [{ text: "已保存", style: { fontWeight: 700 } }, { text: " 2026-10-17", style: { fontSize: 12 } }]`。片段样式继承内容块默认值。每行片段共用基线,行高按该行最大字号计算,样式边界不强制换行。英文优先空格断行,中文、超长单词和 URL 可以在字素之间断行。`Intl.Segmenter` 将组合重音、肤色 emoji、ZWJ 序列、国旗和键帽保留为完整字素;跨源片段的字素使用首字符的样式。排版不读取 DOM 或字体加载结果。
291
+ `service` 节点在浅色主题下的渲染结果:
269
292
 
270
- 每个内容块的 `style` 支持正有限数 `fontSize`、100–900 的整数字重 `fontWeight`、十六进制 `color` 和 `align`(`left`、`center`、`right`)。内容整体样式支持非负有限数 `padding` 和 `blockGap`。字号与间距单位均为图解单位。默认标题字号/字重为 20/700,正文为 14/400,辅助文字为 12/400;内边距为 16,内容块间距为 14,行高至少为 20 或字号的 1.35 倍。辅助文字使用主题弱化色,作者显式指定的内容颜色保持原值。
293
+ ![固定尺寸的 Order service 节点,包含标题、辅助文字、正文与蓝色右对齐段落](https://cdn.jsdelivr.net/npm/flowsvg@0.1.1/docs/images/node-content.png)
271
294
 
272
- 列表格式为 `{ type: "list", marker?: "none" | "bullet" | "number", items: ContentListItem[], style?: ContentListStyle }`,默认无标记(`none`)。每个条目可省略 `tags`、`status`、`body`、`auxiliary`、正文 `style`、`auxiliaryStyle` 和 `statusStyle`。列表 `style` 提供正文样式及非负有限 `itemGap`(默认 12)。条目正文样式覆盖列表正文样式;辅助文字保留弱化的 12 单位默认样式,可由 `auxiliaryStyle` 覆盖。圆点和编号使用统一标记列,容纳多位编号后留出 8 单位间距。正文续行和辅助文字均从正文列开始。标记与第一个存在的文字部分共用基线。缺省部分及完全空的条目不占高度或条目间距;显式空字符串和空行仍保留一行。例如:
295
+ 文字字段接受字符串或 `ContentTextSpan[]`:`{ text, style?: { fontSize, fontWeight, color } }`。片段继承内容块样式并共用基线,对齐由内容块决定。每行使用最大字号;换行保留显式空行和完整 Unicode 字素,优先在英文空格处断行,必要时拆分长单词或 URL。跨片段的字素采用首字符样式。排版使用宽度估算,不测量 DOM。
296
+
297
+ 文字样式:有限正数 `fontSize`、100–900 整数 `fontWeight`、十六进制 `color`、`align`(`left`、`center`、`right`)。内容的 `padding`、`blockGap` 为非负有限数。默认值按图解单位计:标题 20/700、正文 14/400、辅助文字 12/400、内边距 16、块间距 14;行高为 `max(20, fontSize × 1.35)`。显式颜色覆盖主题默认值。
298
+
299
+ 列表接受 `marker`(默认 `none`,可选 `bullet`、`number`)、`items` 和 `style`。条目支持 `tags`、`status`、`body`、`auxiliary`、`style`、`auxiliaryStyle`、`statusStyle`。列表样式设置正文样式和非负有限数 `itemGap`(默认 12),条目正文样式可覆盖;辅助文字默认使用 12 单位弱化色。标记共用一列,与正文间隔 8 单位;正文续行与辅助文字对齐。标记对齐首个实际文字的基线。缺省条目/部分不占空间,显式空文字保留一行。
273
300
 
274
301
  ```ts
275
- { type: "list", marker: "number", style: { itemGap: 16 }, items: [
302
+ import type { ContentBlock } from "flowsvg";
303
+
304
+ const steps: ContentBlock = { type: "list", marker: "number", style: { itemGap: 16 }, items: [
276
305
  { body: "校验请求。", auxiliary: "输入检查" },
277
306
  { body: [{ text: "保存", style: { fontWeight: 700 } }, { text: "订单。" }] }
278
- ] }
307
+ ] };
279
308
  ```
280
309
 
281
- 文字标签格式为 `{ text: ContentText, style?: { fontSize, fontWeight, color, fill } }`,默认字号 11。底色在文字左右各留 6 单位、上下各留 2 单位,圆角半径 3。列表 `style.tagGap` 为非负有限数,默认 6,用于标签之间、标签行之间及同排状态之前的间距。默认底色和文字颜色随主题变化,显式颜色保持原值。状态默认使用 12 单位的主题强调色;`statusStyle` 仅支持 `fontSize`、`fontWeight` 和 `color`,状态始终右对齐。单排标签与单行状态的自然宽度能容纳时共用基线;否则标签先换行,状态从全部标签之后开始。长标签和长状态沿用混合样式的字素换行规则。正文位于元信息之后,辅助文字位于正文之后。缺省部分不占空间。例如:
310
+ 将 `steps` 放入 300 × 180 的矩形,设置 `content: { blocks: [steps] }`:
311
+
312
+ ![包含辅助文字和加粗保存文字的两个编号步骤](https://cdn.jsdelivr.net/npm/flowsvg@0.1.1/docs/images/numbered-list.zh-CN.png)
313
+
314
+ 文字标签接受 `{ text, style?: { fontSize, fontWeight, color, fill } }`。默认字号 11,水平/垂直内边距 6/2,圆角 3。列表 `tagGap` 默认 6,须为非负有限数。状态靠右,默认使用 12 单位强调色;`statusStyle` 支持 `fontSize`、`fontWeight`、`color`。宽度足够时标签与状态共用基线;否则先换行标签,再将状态放到下方。正文、辅助文字随后排列。显式颜色和底色覆盖主题默认值。
282
315
 
283
316
  ```ts
284
- { type: "list", items: [{
317
+ import type { ContentBlock } from "flowsvg";
318
+
319
+ const records: ContentBlock = { type: "list", items: [{
285
320
  tags: [{ text: "world" }, { text: "experience", style: { fill: "#eadfff", color: "#57319b" } }],
286
321
  status: "new", body: "Alice joined Google", auxiliary: "Mar 2026"
287
- }] }
322
+ }] };
288
323
  ```
289
324
 
290
- 节点尺寸保持固定。矩形应用作者内边距。圆角矩形先在四边额外内缩 4 个图解单位,再应用内边距。圆柱上下各预留当前弧面半径的两倍,再应用内边距;弧面半径为 `min(14, height / 4)`。安全区宽高不小于零,形状留白与内边距占满节点时仍可求值。内容从安全区顶部开始,按确定性字宽估算换行,保留显式换行和空行。SVG 在内容安全区裁剪文字。宽度或高度不足时分别产生 `text-overflow` 警告,包含节点 ID、`nodes[index].content` 路径和不足方向。空安全区仍可求值和导出。被裁剪的内容不扩大节点命中范围。普通标签保留现有溢出行为。
325
+ 将 `records` 放入 300 × 180 的矩形,设置 `content: { blocks: [records] }`:
291
326
 
292
- `EvaluatedNode.contentLayout` 提供 `safeArea`、有序 `lines`、标签底色 `tags`、`widthOverflow` 和 `heightOverflow`。每行提供文本、基线坐标、估算宽度、字号、字重、颜色、对齐方式、语义角色(`title`、`body`、`auxiliary`、`marker`、`tag` 或 `status`)、源内容块索引 `blockIndex`,以及列表行的可选条目索引 `itemIndex`。标签文字行另有 `tagIndex`。每个标签底色提供绝对 `x`、`y`、`width`、`height`、最终 `fill`、作者颜色来源 `explicitFill` 及源索引 `blockIndex`/`itemIndex`/`tagIndex`。每行 `spans` 提供各片段的文本、左侧绝对坐标、共用基线、估算宽度及最终字号/字重/颜色;`explicitColor` 表示作者是否在内容块或源片段指定了颜色。浏览器 SVG 与静态导出使用相同结果。内容跟随当前绝对几何(包括尺寸过渡和父分组移动)、祖先显隐和有效透明度。内容不改变端口、连线路由、传递几何或节点父级规则。可运行[标题与段落示例](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/node-content.html)和[列表示例](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/list-content.html)。
327
+ ![记录包含 world 和 experience 标签、new 状态、正文与日期](https://cdn.jsdelivr.net/npm/flowsvg@0.1.1/docs/images/record-tags.png)
293
328
 
294
- 三种适用形状均支持初始节点内容、混合样式文字片段、列表和时间线替换。标签、状态及显式、传递和悬停高亮下的默认颜色可读性均已支持。
329
+ 内容保持节点原尺寸。安全区在内边距之外额外避让:圆角矩形每侧 4 单位,圆柱上下各两倍端盖半径(`min(14, height / 4)`);宽高最小为零。SVG 按安全区裁剪,宽/高不足时报告带节点 ID、`nodes[index].content` 路径和方向的 `text-overflow`。裁剪文字不扩大命中范围,普通标签沿用独立的溢出规则。
295
330
 
331
+ `EvaluatedNode.contentLayout` 提供安全区、文字行/片段、标签底色、可选图形几何及宽高溢出标志。几何使用绝对坐标,跟随节点尺寸、父级移动、显隐和透明度。文字行保留块/条目/标签索引,`explicitColor`、`explicitFill` 标识作者配色。浏览器与静态 SVG 共用布局;内容不改变端口、路由、传递几何或父级关系。参见[段落](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/node-content.html)与[列表](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/list-content.html)示例。
296
332
 
297
333
  ### 节点内的实体关系小图
298
334
 
299
- `graph` 内容块在支持内容的形状内部绘制无向实体关系小图。作者指定局部坐标,SDK 不自动排列实体。可从 `flowsvg` 导入 `ContentGraphBlock`:
335
+ `graph` 内容块按作者指定的局部坐标绘制无向实体关系图:
300
336
 
301
337
  ```ts
338
+ import type { ContentGraphBlock } from "flowsvg";
339
+
302
340
  const entities: ContentGraphBlock = {
303
341
  type: "graph", height: 130,
304
342
  nodes: [
@@ -310,13 +348,17 @@ const entities: ContentGraphBlock = {
310
348
  };
311
349
  ```
312
350
 
313
- `height` 为正数,预留固定高度,之后应用普通内容块间距。实体中心 `x`/`y` 是相对该区域左上角的非负有限坐标,单位为图解单位。ID 在单个图形块内唯一,关系的两个端点引用该块中的实体 ID。标签使用转义的 12 单位纯文本,居中放在半径为 4 的圆点上方。选中实体使用强调色圆点和粗体标签;关系的两个端点均选中时,关系使用强调色。文字与图元共用父节点内容裁剪,并跟随主题与高亮配色。坐标或标签超出图形块区域时,SDK 报告对应方向的溢出警告,后续内容块的位置保持不变。作者需在圆点上方为标签预留空间。实体没有独立交互身份,点击或悬停均命中父节点。
351
+ 将 `entities` 放入 280 × 180 的矩形,设置 `content: { blocks: [entities] }`:
352
+
353
+ ![实体关系图中 Alice 和 Google 高亮,ML project 未选中](https://cdn.jsdelivr.net/npm/flowsvg@0.1.1/docs/images/entity-graph.png)
314
354
 
315
- 存在图形块时,`contentLayout.graphs` 提供按 `blockIndex` 标识的绝对圆点/线段几何、局部 `highlighted` 状态和最终颜色。图形标签进入 `contentLayout.lines`。在节拍起点整体替换 `content`,即可切换局部选中状态。[Hindsight 完整示例](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/hindsight.html) 展示三幕、25 步故事,包括分组端点与实体选中状态变化。
355
+ `height` 预留固定正数高度。实体中心 `x`/`y` 是相对区域左上角的非负有限坐标。ID 仅在块内唯一,关系端点引用块内 ID。12 单位标签位于半径为 4 的圆点上方。高亮实体使用强调色圆点与粗体;关系两端均高亮时使用强调色。溢出按方向报告,不移动后续内容块。图形共用父节点裁剪与主题,交互命中父节点。
356
+
357
+ `contentLayout.graphs` 按 `blockIndex` 提供绝对几何和高亮状态,标签进入 `contentLayout.lines`。在节拍起点替换 `content` 可切换选中状态。参见 [Hindsight 示例](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/hindsight.html)。
316
358
 
317
359
  ### 编写完整 Facts 时间线
318
360
 
319
- 先指定固定形状尺寸,再按顺序添加内容块。记录使用 `marker: "none"`,能力清单使用 `"bullet"`,步骤使用 `"number"`。条目只需提供实际存在的部分。日期是普通文字;小字号片段可让日期与正文共用基线。下面的完整文档可以直接传给任一宿主:
361
+ 先确定尺寸,再排列内容块。记录用无标记列表,能力清单用圆点,步骤用编号。下例可用于全部四种接入方式:
320
362
 
321
363
  ```ts
322
364
  import { createDiagram, type DiagramDocument, type NodeContent } from "flowsvg";
@@ -351,9 +393,13 @@ console.log(diagram.evaluateAt(1000).warnings);
351
393
  const svg = diagram.renderSvg(1000, { theme: "dark", reducedMotion: true });
352
394
  ```
353
395
 
354
- 分享文档前检查 `contentLayout.safeArea` 与 `frame.warnings`。内容超出安全区时,作者需要增大尺寸或精简文字。SDK 裁剪溢出部分,不自动调整节点尺寸或缩小字号。切换主题或高亮时优先使用默认角色颜色;显式配色由作者负责。内容整体只有节点级交互目标,不提供内部链接、按钮、图片、HTML 或独立时间线目标。
396
+ 左图为 0 ms 的浅色主题初始画面,右图为 1000 ms 的深色主题替换后画面:
397
+
398
+ ![Facts 时间线在记录替换前后的浅色与深色主题画面](https://cdn.jsdelivr.net/npm/flowsvg@0.1.1/docs/images/facts-timeline.zh-CN.png)
355
399
 
356
- [正式内容时间线](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/content-story.html) 将 Facts、服务能力和编号步骤与嵌套分组移动、节点变宽、显式端口、三种连线路由、同步传递及聚焦组合。[共享文档](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/content-story.js) 同时用于 React 和 Vue 示例;原生与 Web Component 在同一页面并列展示。正常、窄节点及长文字链接展示固定安全区裁剪,主题与减少动画控件传递相同视觉选项。四种宿主共用内置播放/暂停、速度、前后节拍、场景及进度控件。
400
+ 分享前检查 `frame.warnings`,按需增大节点或精简文字;SDK 不自动调整尺寸或缩小字号。优先使用默认颜色保持主题与高亮下的对比度。内容只有节点级交互,不支持内部链接、按钮、图片或 HTML。
401
+
402
+ [内容时间线](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/content-story.html)组合 Facts、列表、节点更新、传递与聚焦。[共享文档](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/content-story.js)也用于 Vue、React 示例。
357
403
 
358
404
  每个节拍包含正数 `durationMs` 和 `actions`,可选 `transitionMs` 与 `caption`。过渡时长默认 `min(250, durationMs)`,取值范围为 `[0, durationMs]`;`caption` 省略时继承前文,空字符串可清空。
359
405
 
@@ -366,7 +412,7 @@ const svg = diagram.renderSvg(1000, { theme: "dark", reducedMotion: true });
366
412
  | `updateEdge` | `value` 支持 `label`、`stroke`、`strokeWidth`、`opacity` |
367
413
  | `focus` | `target` 为节点/分组 ID;`null` 恢复完整视口 |
368
414
 
369
- `updateNode.value.content` 在节拍起点整体替换内容,即使 `transitionMs` 非零也立即生效。结果继承到后续节拍和场景。更新 `label` 切换到普通标签模式;更新 `content` 切换到节点内容模式。`content: null` 清空任一文字模式,只保留形状,不恢复历史标签,也不改变节点尺寸。同一节拍不能同时赋值 `label` 与 `content`,也不能重复赋值任一模式;校验错误包含字段位置。跳转、暂停、播放速度及减少动画共用这一内容更新时间边界。
415
+ `updateNode.value.content` 在节拍起点整体替换内容,不受过渡时长影响,结果继承到后续节拍与场景。`label` 切换到普通标签,`content` 切换到节点内容;`content: null` 清空任一模式,不恢复旧标签或改变尺寸。同一节拍同时赋值两种模式或重复赋值会报告字段路径错误。
370
416
 
371
417
  ```ts
372
418
  // 每个 value 分别用于不同节拍的 updateNode 动作。
@@ -377,13 +423,13 @@ const simpleLabel: NodeVisualState = { label: "Summary" };
377
423
  const clearText: NodeVisualState = { content: null };
378
424
  ```
379
425
 
380
- 所有场景共享以毫秒计的全局时间线,后一场景继承前一场景最终状态。标签与说明文字在节拍起点切换,可插值属性在过渡阶段变化。节拍区间左闭右开,最终时间返回完成后的最后画面。隐藏节点或分组会同步遮蔽关联连线;重新显示时按对象各自保存的状态恢复。
426
+ 场景共用毫秒时间线,继承前一场景的最终状态。文字在节拍起点切换,插值属性在过渡期变化。节拍左闭右开,最终时间返回完成画面。隐藏节点/分组会遮蔽关联连线,重新显示时恢复各连线保存的显隐状态。
381
427
 
382
- 一个节拍可以同时在多条不同连线上传递。所有传递在节拍起点开始,以 `durationMs` 上相同的线性完成比例推进,在节拍结束时消失。因此,不同长度路径上的实际速度可以不同。只要当前传递仍使用某个端点,该节点就保持高亮。下一节拍会替换整组传递,跨场景边界也相同。传递高亮不会继承;显式高亮和普通样式继续按原有规则继承。
428
+ 每拍可在多条不同连线上各传递一次。标记共用 `durationMs` 内的线性进度,因此路径越长,移动越快。标记和端点临时高亮在拍末消失,下一拍提供新的一组传递;显式高亮和普通样式继续继承。
383
429
 
384
- ## API
430
+ 传递 `text` 按纯文本转义,始终水平,胶囊宽度按固定规则估算。文字不替换连线标签,也不产生独立交互目标;空字符串仍显示胶囊。
385
431
 
386
- 传递文字属于单次动作,不替换连线的静态 `label`。胶囊宽度按 Unicode 码点和固定宽度估算,并保留内边距,不依赖浏览器测量。文字在所有路由上保持水平,并作为纯文本转义。胶囊不提供独立指针操作或键盘焦点;显式提供空字符串仍显示胶囊。
432
+ ## API
387
433
 
388
434
  ### 核心 API
389
435
 
@@ -400,11 +446,11 @@ const clearText: NodeVisualState = { content: null };
400
446
  | `diagram.hitTest(timeMs, point, options?)` | 在文档坐标中返回 `{ id, type: "node" \| "edge" }` 或 `null` |
401
447
  | `wrapText(label, maxWidth, typography?)` | 按固定字宽估算换行,返回文本行数组 |
402
448
 
403
- `EvaluateOptions` 为 `{ theme?: "light" | "dark"; reducedMotion?: boolean }`。有限时间限制在 `[0, totalDuration]`,非有限时间按零处理。命中忽略隐藏或零透明度对象,优先级依次为普通节点、连线、分组背景。悬停所有节点默认只高亮边框,保留当前求值后的背景色,包括播放中的时间线高亮;设置 `hoverFill: true` 后才绘制背景反馈。
449
+ `EvaluateOptions`:`{ theme?: "light" | "dark"; reducedMotion?: boolean }`。时间限制在 `[0, totalDuration]`,非有限值按零处理。命中跳过隐藏/零透明度对象,依次检查普通节点、连线、分组背景。
404
450
 
405
- `DiagramFrameState` 包含 `timeMs`、`sceneId`、`beatId`、`beatProgress`、`nodes`、`edges`、`camera`、可选 `caption` 和 `warnings`。活动的 `EvaluatedEdge` 可能包含 `flow`,其中提供方向、节拍线性进度、文档坐标中的标记位置和可选消息 `text`;节拍结束、连线隐藏或启用减少动画时不提供标记。求值后的节点坐标为绝对文档坐标。静态文档的场景和节拍 ID 为 `null`;节拍 ID 格式为 `${scene.id}:${beatIndex}`,索引从零开始。
451
+ `DiagramFrameState` 包含时间、场景/节拍 ID、进度、求值后的节点/连线、镜头、说明文字及警告。坐标为绝对坐标。静态图的场景/节拍 ID 为 `null`;节拍 ID 为 `${scene.id}:${beatIndex}`,索引从零开始。活动连线的 `flow` 提供方向、进度、位置及可选文字;拍末、隐藏或减少动画时不显示标记。
406
452
 
407
- 主要公开类型包括 `DiagramDocument`、`DiagramNode`、`DiagramEdge`、`Scene`、`Beat`、`Action`、`Diagram`、`DiagramFrameState`、`EvaluateOptions`、`ValidationIssue` 和 `LayoutWarning`。类型声明及关键成员 JSDoc 随包提供。
453
+ 类型及成员 JSDoc 随包提供:`DiagramDocument`、`DiagramNode`、`DiagramEdge`、`Scene`、`Beat`、`Action`、`Diagram`、`DiagramFrameState`、`EvaluateOptions`、`ValidationIssue`、`LayoutWarning`。
408
454
 
409
455
  ### 播放器 API
410
456
 
@@ -421,13 +467,13 @@ const clearText: NodeVisualState = { content: null };
421
467
  | `getState()` | 返回 `PlayerState`:`timeMs`、`totalDuration`、`playing`、`speed`、`frame` |
422
468
  | `destroy()` | 幂等清理 DOM、监听器与调度;之后停止事件,状态仍可读取 |
423
469
 
424
- 创建时默认暂停在零时刻,内置控件默认开启。页面隐藏时暂停,重新可见后保持暂停;播放结束停在最终画面。静态图禁用时间控件。框架 handle 提供上述控制和状态方法,省去 `element` 与 `destroy`,由框架管理宿主与清理。
470
+ 创建时暂停在零时刻,默认显示控件。页面隐藏时暂停,恢复后保持暂停;播放在终点停止,静态图禁用时间控件。框架实例提供表中方法,但不暴露 `element` 和 `destroy`。
425
471
 
426
- 播放或暂停时,悬停可见节点的形状、普通标签或结构化内容默认只高亮当前节点边框,不联动相邻节点或连线。设置 `hoverFill: true` 后才同时高亮背景与结构化内容。鼠标移出只解除悬停原因;传递与显式高亮仍有效时,节点继续高亮。全部原因结束后,节点显示当前普通样式。画面更新后,播放器重新检查未移动的指针;指针取消、文档成功替换和销毁会清除悬停。
472
+ 悬停形状、标签或内容时高亮当前节点边框。`hoverFill: true` 同时高亮背景与内容,也适用于分组。移出只解除悬停高亮,活动的传递/显式高亮仍保留。画面更新会重新检查未移动的指针;指针取消、文档成功替换和销毁会清除悬停。
427
473
 
428
- 默认标题、正文、列表标记、辅助文字、文字标签与状态在浅深主题及显式、传递、悬停高亮下保持可读。高亮过渡的中间帧按当前求值底色选择具有足够对比度的默认文字颜色。作者指定的片段或内容块颜色及标签底色保持原值。高亮不改变文字几何、安全区或命中范围。内容内部没有独立 ID、键盘焦点、链接或按钮;点击内容返回节点身份,被裁剪的文字不增加命中区域。内容替换、文字模式切换与节点尺寸更新后,悬停只作用于当前画面;成功替换文档会清除旧文档的悬停。
474
+ 默认内容颜色跟随主题与高亮底色,作者配色和标签底色保持原值。高亮不改变布局或命中范围。内容交互返回父节点,裁剪文字不增加命中区域。
429
475
 
430
- `PlayerState.frame` 保留核心画面,不包含悬停状态。静态 SVG 导出也不包含悬停;没有悬停节点时,浏览器输出与相同时间的导出一致。
476
+ `PlayerState.frame` 和静态 SVG 不含悬停;没有悬停时,浏览器输出与同时间导出一致。
431
477
 
432
478
  ### 事件
433
479
 
@@ -441,9 +487,9 @@ const clearText: NodeVisualState = { content: null };
441
487
  | `error` | `PlayerErrorDetail`:保留的播放器状态与 `error` |
442
488
  | `hover` / `click` | `PlayerInteractionDetail` 或 `null`;也提供 `objecthover` / `objectclick` 同义事件 |
443
489
 
444
- 交互 detail 包含 `id`、`type`、文档坐标 `point`、`timeMs`、`sceneId`、`beatId`;未命中为 `null`。Vue、React 和 Web Component 的包装层转发前四类事件。Web Component 挂载前的错误可能没有播放器状态。
490
+ 交互 detail 包含 `id`、`type`、`point`(文档坐标)、`timeMs`、`sceneId`、`beatId`,未命中为 `null`。包装层转发四类播放/错误事件;Web Component 挂载前的错误可能没有播放器状态。
445
491
 
446
- 指针移动时发送 `hover`;画面更新导致未移动的指针命中其他对象时,也发送该事件。指针移出或取消时发送 `null`。
492
+ 指针移动会发送 `hover`;画面更新导致未移动的指针命中其他对象时也发送。移出或取消时发送 `null`。
447
493
 
448
494
  ```ts
449
495
  import type { PlayerEventMap } from "flowsvg/browser";
@@ -460,7 +506,19 @@ player.element.addEventListener("timeupdate", handleTimeUpdate);
460
506
 
461
507
  ## 主题与无障碍
462
508
 
463
- 主题优先级为显式选项 → 文档主题 → 浅色;核心不读取系统主题。核心的 `reducedMotion` 默认关闭,浏览器未指定时跟随 `prefers-reduced-motion` 并响应变化。减少动画让过渡立即完成,保留总时长与时间位置。
509
+ 主题:显式选项 → 文档主题 → 浅色,核心不读取系统偏好。浏览器动画默认跟随 `prefers-reduced-motion`,核心默认正常动画。减少动画会立即完成过渡并隐藏传递标记,保留总时长。
510
+
511
+ 默认样式使用中性边框与连线,以蓝色突出活动对象:
512
+
513
+ | 颜色 | 浅色 | 深色 |
514
+ | --- | --- | --- |
515
+ | 背景 / 节点填充 | `#ffffff` / `#ffffff` | `#101828` / `#1d2939` |
516
+ | 节点边框 / 文字 | `#cfd8e3` / `#1d2939` | `#475467` / `#f2f4f7` |
517
+ | 连线 | `#8892a4` | `#98a2b3` |
518
+ | 高亮填充 / 边框 | `#eaf1ff` / `#2a63c8` | `#233b60` / `#8ab4f8` |
519
+ | 传递标记 / 标记文字 | `#2a63c8` / `#ffffff` | `#8ab4f8` / `#101828` |
520
+
521
+ 默认节点边宽为 1.2 图解单位,连线边宽为 2,圆角矩形的圆角半径上限为 8。节点标签使用无描边文字;连线标签保留背景描边,隔开穿过文字的路径。图解文字使用系统字体,并提供中文字体回退。显式样式替换普通默认值;时间线和悬停高亮叠加在普通样式上,解除高亮后恢复当前普通颜色。
464
522
 
465
523
  导入 `flowsvg/styles.css` 可获得控件布局与焦点样式。CSS 变量只定制控件,图解本身颜色由核心主题和文档样式决定:
466
524
 
@@ -474,52 +532,16 @@ player.element.addEventListener("timeupdate", handleTimeUpdate);
474
532
  }
475
533
  ```
476
534
 
477
- 原生宿主可添加 `flowsvg` class 使用上例,框架组件会自动添加。聚焦画布后,Space 播放/暂停,左右方向键逐步移动;表单控件保留原生按键行为。说明文字使用礼貌的 live region,SVG 具有独立的可访问名称。
535
+ 原生宿主添加 `flowsvg` 类即可应用变量,框架组件自动添加。画布聚焦后,Space 播放/暂停,方向键逐拍移动;表单控件保留原生按键。说明文字使用礼貌播报区,SVG 提供可访问名称。
478
536
 
479
537
  ## 校验与错误
480
538
 
481
- 未知字段、无效引用、循环分组、重复节拍赋值、非法颜色以及无效尺寸/时长会在加载时拒绝。文本按纯文本转义。需要预先检查外部 JSON 时,先调用 `validateDocument`,根据每条 issue 的 `path` 和 `message` 展示错误。
539
+ 校验拒绝未知字段、错误引用、循环分组、重复节拍赋值、非法颜色和尺寸/时长。文本按纯文本转义。检查外部 JSON 时,调用 `validateDocument` 并读取各条 `path`、`message`。
482
540
 
483
- `createDiagram` 和无效初始文档的 `createPlayer` 会抛出 `DiagramValidationError`,其 `issues` 包含详细路径。已有播放器的 `load` 通过 `error` 事件报告校验失败并保留旧状态。文字高度溢出属于非阻塞布局警告,读取 `frame.warnings` 后决定是否调整节点尺寸。
484
-
485
- 一个节拍内每条连线最多包含一个 `flow`,方向或文字不同也不能重复。重复连线会在后一动作的 `target` 报告 `duplicate beat assignment`,其他字段无效也不影响重复检查。传递可以与同一连线的显隐、显式高亮和普通样式动作共存;各类动作继续遵循各自的校验规则。
486
-
487
- ## 开发与验证
488
-
489
- 以下命令在源码仓库根目录执行。完整开发环境使用 Node.js 22.12+ 与 pnpm 10.12.1;Vue/React 示例基于 Vite 7。
490
-
491
- | 命令 | 用途 |
492
- | --- | --- |
493
- | `pnpm install` | 安装工作区依赖 |
494
- | `pnpm build` | 构建 SDK 的 JS、类型与样式 |
495
- | `pnpm typecheck` | 检查核心、浏览器和框架入口类型 |
496
- | `pnpm test` | 运行 Vitest 行为测试 |
497
- | `pnpm --filter flowsvg-vue-example dev` | 启动 Vue 示例 |
498
- | `pnpm --filter flowsvg-react-example dev` | 启动 React 示例 |
499
- | `pnpm release:verify` | 从缺失的 `dist` 打包,检查文件白名单与全部导出,再验证无框架导入、严格消费者类型及四种宿主生命周期 |
500
- | `pnpm release` | 运行全部发布检查与 npm 预演,将已验证的 tarball 保存在 `dist/releases/`,不上传 |
501
- | `pnpm release:publish` | 检查 npm 登录状态,重新运行全部检查与预演,然后上传已验证的 tarball |
541
+ `createDiagram` 和首次 `createPlayer` 遇到非法文档会抛出 `DiagramValidationError`,`issues` 包含字段路径。后续 `load` 失败时发送 `error`,保留旧状态。文字溢出不阻止渲染,可读取 `frame.warnings`。
502
542
 
503
- 维护者请参照 [npm 发布指南](https://github.com/yokingma/FlowSVG/blob/HEAD/docs/RELEASING.md)。SDK 打包会删除并重建 `dist`,必须在示例构建之前完成。
504
-
505
- 构建后,从仓库根目录启动 Node.js 静态服务器:
506
-
507
- ```sh
508
- pnpm dlx http-server@14.1.1 . -p 4175 -c-1
509
- ```
510
-
511
- 首次运行会下载 CLI,不会新增项目依赖。打开 <http://localhost:4175/apps/native-example/> 运行原生示例。浏览器验收访问 `/packages/flowsvg/tests/browser-*.html` 下的页面:`controls`、`lifecycle`、`visual-updates`、`focus`、`accessibility`、`web-component`、`flow-text`、`parallel-flows`。检查相应的 `window.browserTestResults`、`window.browserFocusResults`、`window.browserAccessibilityResults`、`window.browserWebComponentResults` 或 `window.browserFlowTextResults`,每条结果的 `passed` 应为 `true`。
512
-
513
- 真实指针悬停验收访问 `/packages/flowsvg/tests/browser-hover.html`。分别悬停起点形状与溢出文字,使用传递中点和传递结束控件,检查鼠标移出时保留活动传递高亮,传递结束后恢复最新普通样式。其余控件用于检查固定指针下的画面更新、重载清理和销毁清理。
514
-
515
- 自动减少动画的验收:打开 accessibility 页,切换浏览器的减少动画媒体模拟,再调用 `window.verifyMotionPreference(true)` 或 `false`,验证系统偏好变化与同选项 SVG 导出一致。
516
-
517
- 高频播放时复用编译结果,避免每次渲染都重建播放器;大型图解应在目标设备上测量帧耗时。变更记录见 [CHANGELOG](CHANGELOG.md),实现约定见 [PATTERNS](PATTERNS.md)。
518
-
519
- `tests/browser-parallel-flows.html` 提供先三条、后两条传递的可播放示例,并比较跨场景边界、主题和减少动画选项下的公开画面与 SVG。
543
+ 同一连线重复声明 `flow` 时,后一 `target` 报告 `duplicate beat assignment`,与方向、文字及其他字段错误无关。传递可与同一连线的显隐、显式高亮和样式动作共存。
520
544
 
521
545
  ## License
522
546
 
523
547
  [MIT](LICENSE)。可使用、修改及分发;保留许可证与版权声明。
524
-
525
- 访问[节点内容指针验收页](https://github.com/yokingma/FlowSVG/blob/HEAD/packages/flowsvg/tests/browser-node-content.html),深色模式添加 `?theme=dark`。检查三种形状、文字标签、长文字裁剪、显式/传递/悬停分层、未移动指针下的内容/文字模式/节点尺寸更新、主题文档替换与销毁。
@@ -0,0 +1,48 @@
1
+ /**
2
+ * React adapter for component lifecycle, props, events and imperative controls.
3
+ * Playback/rendering reuse browser; imports do not create DOM, and the player is created only on mount.
4
+ */
5
+ import { type CSSProperties } from "react";
6
+ import { type PlayerErrorDetail, type PlayerState } from "../browser/player.js";
7
+ import type { DiagramDocument, ThemeName } from "../core/model.js";
8
+ /** React document, visual options and callbacks; document reference changes trigger a reload. */
9
+ export interface FlowPlayerProps {
10
+ document: DiagramDocument;
11
+ /** Positive playback multiplier; omit to use the player default. */
12
+ speed?: number;
13
+ theme?: ThemeName;
14
+ controls?: boolean;
15
+ /** Defaults to false: hover marks borders only. True also highlights backgrounds, including groups. */
16
+ hoverFill?: boolean;
17
+ reducedMotion?: boolean;
18
+ /** Initial position in milliseconds, applied once; use seek afterward, and document reloads reset to zero. */
19
+ initialTimeMs?: number;
20
+ className?: string;
21
+ style?: CSSProperties;
22
+ onStepChange?: (state: PlayerState) => void;
23
+ onTimeUpdate?: (state: PlayerState) => void;
24
+ onPlayStateChange?: (state: PlayerState) => void;
25
+ onError?: (detail: PlayerErrorDetail) => void;
26
+ }
27
+ /** Imperative controls for a mounted component; calls before mounting or after unmounting throw a not-mounted error. */
28
+ export interface FlowPlayerHandle {
29
+ /** Resume from the current position when playback can advance. */
30
+ play(): void;
31
+ /** Pause while retaining the current frame. */
32
+ pause(): void;
33
+ /** Seek in milliseconds, clamping to the timeline; non-finite values become zero. */
34
+ seek(timeMs: number): void;
35
+ /** Pause and jump to a beat boundary; backward from inside a beat goes to that beat's start. */
36
+ step(direction: 1 | -1): void;
37
+ /** Set a finite positive multiplier; ignore invalid values. */
38
+ setSpeed(speed: number): void;
39
+ /** Reset/pause at zero on success; emit an error and retain playback state if validation fails. */
40
+ load(document: DiagramDocument): void;
41
+ /** Read the current browser-player snapshot. */
42
+ getState(): PlayerState;
43
+ }
44
+ /**
45
+ * SSR renders only a host container; an effect mounts the browser player and unmount cleanup releases it.
46
+ * Document reference changes call load; visual/control options rebuild the instance, with initial positioning applied once.
47
+ */
48
+ export declare const FlowPlayer: import("react").ForwardRefExoticComponent<FlowPlayerProps & import("react").RefAttributes<FlowPlayerHandle>>;