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.
- package/CHANGELOG.md +26 -1
- package/PATTERNS.md +12 -1
- package/README.md +139 -117
- package/README.zh-CN.md +139 -117
- package/dist/adapters/react.d.ts +48 -0
- package/dist/adapters/react.js +126 -0
- package/dist/adapters/vue.d.ts +132 -0
- package/dist/adapters/vue.js +161 -0
- package/dist/adapters/web-component.d.ts +99 -0
- package/dist/adapters/web-component.js +396 -0
- package/dist/browser/player.d.ts +69 -0
- package/dist/browser/player.js +353 -0
- package/dist/browser.d.ts +4 -69
- package/dist/browser.js +2 -353
- package/dist/core/color.d.ts +3 -0
- package/dist/core/color.js +18 -0
- package/dist/core/diagram.d.ts +38 -0
- package/dist/core/diagram.js +391 -0
- package/dist/core/edge-geometry.d.ts +32 -0
- package/dist/core/edge-geometry.js +382 -0
- package/dist/{core.d.ts → core/model.d.ts} +2 -52
- package/dist/core/model.js +1 -0
- package/dist/{node-content-paint.d.ts → core/node-content-paint.d.ts} +2 -2
- package/dist/{node-content-paint.js → core/node-content-paint.js} +11 -20
- package/dist/{node-content.d.ts → core/node-content.d.ts} +1 -1
- package/dist/core/svg-number.d.ts +5 -0
- package/dist/core/svg-number.js +7 -0
- package/dist/{theme.d.ts → core/theme.d.ts} +6 -0
- package/dist/core/theme.js +17 -0
- package/dist/{timeline.d.ts → core/timeline.d.ts} +1 -3
- package/dist/{timeline.js → core/timeline.js} +1 -17
- package/dist/core/validation.d.ts +11 -0
- package/dist/core/validation.js +389 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +2 -1
- package/dist/react.d.ts +3 -48
- package/dist/react.js +2 -126
- package/dist/vue.d.ts +3 -132
- package/dist/vue.js +2 -161
- package/dist/web-component.d.ts +3 -103
- package/dist/web-component.js +2 -396
- package/docs/images/entity-graph.png +0 -0
- package/docs/images/facts-timeline.png +0 -0
- package/docs/images/facts-timeline.zh-CN.png +0 -0
- package/docs/images/node-content.png +0 -0
- package/docs/images/numbered-list.png +0 -0
- package/docs/images/numbered-list.zh-CN.png +0 -0
- package/docs/images/quick-start.png +0 -0
- package/docs/images/record-tags.png +0 -0
- package/package.json +2 -1
- package/dist/core.js +0 -1149
- package/dist/theme.js +0 -6
- /package/dist/{player-accessibility.d.ts → browser/accessibility.d.ts} +0 -0
- /package/dist/{player-accessibility.js → browser/accessibility.js} +0 -0
- /package/dist/{player-controls.d.ts → browser/controls.d.ts} +0 -0
- /package/dist/{player-controls.js → browser/controls.js} +0 -0
- /package/dist/{player-lifecycle.d.ts → browser/lifecycle.d.ts} +0 -0
- /package/dist/{player-lifecycle.js → browser/lifecycle.js} +0 -0
- /package/dist/{svg-dom.d.ts → browser/svg-dom.d.ts} +0 -0
- /package/dist/{svg-dom.js → browser/svg-dom.js} +0 -0
- /package/dist/{node-content.js → core/node-content.js} +0 -0
package/README.zh-CN.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
#
|
|
1
|
+
# FlowSVG
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 简体中文
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
FlowSVG(流形)用 SVG 播放逻辑图和架构图。用 JSON 或 TypeScript 定义节点、连线、场景与节拍,即可在浏览器播放或导出静态 SVG。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
SDK 使用 ESM,提供无 DOM 核心及原生浏览器、Vue、React、Web Component 接入。各接入方式共用同一文档和时间线。
|
|
8
8
|
|
|
9
|
-
[安装](#安装) · [快速开始](#快速开始) · [Vue](#vue-集成) · [React](#react-集成) · [原生浏览器](#原生浏览器) · [Web Component](#web-component) · [API](#api) · [
|
|
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
|
-
|
|
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:
|
|
52
|
+
title: "订单请求生命周期",
|
|
53
|
+
viewport: { width: 960, height: 440 },
|
|
63
54
|
nodes: [
|
|
64
|
-
{ id: "client", kind: "roundedRect", x: 40, y:
|
|
65
|
-
{ id: "
|
|
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: [
|
|
68
|
-
|
|
69
|
-
id: "
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
+

|
|
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,
|
|
113
|
+
const finalSvg = renderSvg(diagramDocument, 5200);
|
|
91
114
|
```
|
|
92
115
|
|
|
93
|
-
|
|
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
|
-
|
|
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`
|
|
197
|
+
修改 `theme`、`controls`、`reducedMotion` 或 `hoverFill` 会重建播放器,暂停在零时刻,不再应用 `initialTimeMs`。更新文档需提供新对象引用。实例暴露[播放器 API](#播放器-api)中的七个方法;挂载前或卸载后调用会抛出错误。
|
|
175
198
|
|
|
176
|
-
|
|
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`
|
|
223
|
+
`createPlayer` 替换容器子元素。卸载或复用容器前调用 `destroy()`。原生 HTML 使用包导入路径时,需配置 import map 或构建工具。
|
|
201
224
|
|
|
202
|
-
|
|
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
|
-
|
|
245
|
+
默认注册 `<flowsvg-player>`,重复注册安全。对象赋给 `element.document`;JSON 字符串赋给 HTML `document` 属性;JSON URL 赋给 `src`。URL 相对页面解析,遵循浏览器 CORS 规则。
|
|
225
246
|
|
|
226
|
-
支持 `autoplay`、`controls`、`speed`、`theme`、`reduced-motion
|
|
247
|
+
支持 `autoplay`、`controls`、`speed`、`theme`、`reduced-motion`、`hover-fill`。默认显示控件,关闭自动播放与悬停背景;`controls="false"` 隐藏控件。主题、控件及动画/悬停选项变化时保留时间和播放状态;`autoplay` 则直接控制播放或暂停。移除元素会取消请求并销毁播放器,重新连接时使用保留的文档。
|
|
227
248
|
|
|
228
|
-
|
|
249
|
+
方法:`play`、`pause`、`seek`、`step`、`setSpeed`、`getState`、`destroy`。更换文档使用 `element.document`。挂载前或销毁后,`getState()` 返回 `undefined`。播放与错误事件可冒泡并穿越 Shadow DOM 边界。
|
|
229
250
|
|
|
230
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
291
|
+
`service` 节点在浅色主题下的渲染结果:
|
|
269
292
|
|
|
270
|
-
|
|
293
|
+

|
|
271
294
|
|
|
272
|
-
|
|
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
|
-
|
|
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
|
-
|
|
310
|
+
将 `steps` 放入 300 × 180 的矩形,设置 `content: { blocks: [steps] }`:
|
|
311
|
+
|
|
312
|
+

|
|
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
|
-
|
|
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
|
-
|
|
325
|
+
将 `records` 放入 300 × 180 的矩形,设置 `content: { blocks: [records] }`:
|
|
291
326
|
|
|
292
|
-
|
|
327
|
+

|
|
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`
|
|
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
|
-
`
|
|
351
|
+
将 `entities` 放入 280 × 180 的矩形,设置 `content: { blocks: [entities] }`:
|
|
352
|
+
|
|
353
|
+

|
|
314
354
|
|
|
315
|
-
|
|
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
|
-
|
|
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
|
-
|
|
396
|
+
左图为 0 ms 的浅色主题初始画面,右图为 1000 ms 的深色主题替换后画面:
|
|
397
|
+
|
|
398
|
+

|
|
355
399
|
|
|
356
|
-
|
|
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`
|
|
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
|
-
|
|
428
|
+
每拍可在多条不同连线上各传递一次。标记共用 `durationMs` 内的线性进度,因此路径越长,移动越快。标记和端点临时高亮在拍末消失,下一拍提供新的一组传递;显式高亮和普通样式继续继承。
|
|
383
429
|
|
|
384
|
-
|
|
430
|
+
传递 `text` 按纯文本转义,始终水平,胶囊宽度按固定规则估算。文字不替换连线标签,也不产生独立交互目标;空字符串仍显示胶囊。
|
|
385
431
|
|
|
386
|
-
|
|
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
|
|
449
|
+
`EvaluateOptions`:`{ theme?: "light" | "dark"; reducedMotion?: boolean }`。时间限制在 `[0, totalDuration]`,非有限值按零处理。命中跳过隐藏/零透明度对象,依次检查普通节点、连线、分组背景。
|
|
404
450
|
|
|
405
|
-
`DiagramFrameState`
|
|
451
|
+
`DiagramFrameState` 包含时间、场景/节拍 ID、进度、求值后的节点/连线、镜头、说明文字及警告。坐标为绝对坐标。静态图的场景/节拍 ID 为 `null`;节拍 ID 为 `${scene.id}:${beatIndex}`,索引从零开始。活动连线的 `flow` 提供方向、进度、位置及可选文字;拍末、隐藏或减少动画时不显示标记。
|
|
406
452
|
|
|
407
|
-
|
|
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
|
-
|
|
470
|
+
创建时暂停在零时刻,默认显示控件。页面隐藏时暂停,恢复后保持暂停;播放在终点停止,静态图禁用时间控件。框架实例提供表中方法,但不暴露 `element` 和 `destroy`。
|
|
425
471
|
|
|
426
|
-
|
|
472
|
+
悬停形状、标签或内容时高亮当前节点边框。`hoverFill: true` 同时高亮背景与内容,也适用于分组。移出只解除悬停高亮,活动的传递/显式高亮仍保留。画面更新会重新检查未移动的指针;指针取消、文档成功替换和销毁会清除悬停。
|
|
427
473
|
|
|
428
|
-
|
|
474
|
+
默认内容颜色跟随主题与高亮底色,作者配色和标签底色保持原值。高亮不改变布局或命中范围。内容交互返回父节点,裁剪文字不增加命中区域。
|
|
429
475
|
|
|
430
|
-
`PlayerState.frame`
|
|
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
|
|
490
|
+
交互 detail 包含 `id`、`type`、`point`(文档坐标)、`timeMs`、`sceneId`、`beatId`,未命中为 `null`。包装层转发四类播放/错误事件;Web Component 挂载前的错误可能没有播放器状态。
|
|
445
491
|
|
|
446
|
-
|
|
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
|
-
|
|
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
|
-
|
|
535
|
+
原生宿主添加 `flowsvg` 类即可应用变量,框架组件自动添加。画布聚焦后,Space 播放/暂停,方向键逐拍移动;表单控件保留原生按键。说明文字使用礼貌播报区,SVG 提供可访问名称。
|
|
478
536
|
|
|
479
537
|
## 校验与错误
|
|
480
538
|
|
|
481
|
-
|
|
539
|
+
校验拒绝未知字段、错误引用、循环分组、重复节拍赋值、非法颜色和尺寸/时长。文本按纯文本转义。检查外部 JSON 时,调用 `validateDocument` 并读取各条 `path`、`message`。
|
|
482
540
|
|
|
483
|
-
`createDiagram`
|
|
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
|
-
|
|
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>>;
|