flowsvg 0.0.0-stage → 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 +121 -0
  2. package/LICENSE +21 -0
  3. package/PATTERNS.md +112 -0
  4. package/README.md +546 -2
  5. package/README.zh-CN.md +547 -0
  6. package/dist/adapters/react.d.ts +48 -0
  7. package/dist/adapters/react.js +126 -0
  8. package/dist/adapters/vue.d.ts +132 -0
  9. package/dist/adapters/vue.js +161 -0
  10. package/dist/adapters/web-component.d.ts +99 -0
  11. package/dist/adapters/web-component.js +396 -0
  12. package/dist/browser/accessibility.d.ts +16 -0
  13. package/dist/browser/accessibility.js +36 -0
  14. package/dist/browser/controls.d.ts +41 -0
  15. package/dist/browser/controls.js +80 -0
  16. package/dist/browser/lifecycle.d.ts +6 -0
  17. package/dist/browser/lifecycle.js +31 -0
  18. package/dist/browser/player.d.ts +69 -0
  19. package/dist/browser/player.js +353 -0
  20. package/dist/browser/svg-dom.d.ts +6 -0
  21. package/dist/browser/svg-dom.js +74 -0
  22. package/dist/browser.d.ts +4 -0
  23. package/dist/browser.js +2 -0
  24. package/dist/core/color.d.ts +3 -0
  25. package/dist/core/color.js +18 -0
  26. package/dist/core/diagram.d.ts +38 -0
  27. package/dist/core/diagram.js +391 -0
  28. package/dist/core/edge-geometry.d.ts +32 -0
  29. package/dist/core/edge-geometry.js +382 -0
  30. package/dist/core/model.d.ts +256 -0
  31. package/dist/core/model.js +1 -0
  32. package/dist/core/node-content-paint.d.ts +10 -0
  33. package/dist/core/node-content-paint.js +45 -0
  34. package/dist/core/node-content.d.ts +184 -0
  35. package/dist/core/node-content.js +554 -0
  36. package/dist/core/svg-number.d.ts +5 -0
  37. package/dist/core/svg-number.js +7 -0
  38. package/dist/core/theme.d.ts +21 -0
  39. package/dist/core/theme.js +17 -0
  40. package/dist/core/timeline.d.ts +56 -0
  41. package/dist/core/timeline.js +148 -0
  42. package/dist/core/validation.d.ts +11 -0
  43. package/dist/core/validation.js +389 -0
  44. package/dist/index.d.ts +5 -0
  45. package/dist/index.js +2 -0
  46. package/dist/react.d.ts +3 -0
  47. package/dist/react.js +2 -0
  48. package/dist/styles.css +47 -0
  49. package/dist/vue.d.ts +3 -0
  50. package/dist/vue.js +2 -0
  51. package/dist/web-component.d.ts +3 -0
  52. package/dist/web-component.js +2 -0
  53. package/docs/images/entity-graph.png +0 -0
  54. package/docs/images/facts-timeline.png +0 -0
  55. package/docs/images/facts-timeline.zh-CN.png +0 -0
  56. package/docs/images/node-content.png +0 -0
  57. package/docs/images/numbered-list.png +0 -0
  58. package/docs/images/numbered-list.zh-CN.png +0 -0
  59. package/docs/images/quick-start.png +0 -0
  60. package/docs/images/record-tags.png +0 -0
  61. package/package.json +88 -4
@@ -0,0 +1,45 @@
1
+ const MIN_CONTENT_CONTRAST = 4.5;
2
+ const CONTRAST_DARK_INK = "#000000";
3
+ const CONTRAST_LIGHT_INK = "#ffffff";
4
+ /**
5
+ * Retain theme-specific semantic ink and tag fills; preserve geometry and explicit author colors.
6
+ * Check default ink against the evaluated fill because authored base fills can cross low-contrast transition colors.
7
+ */
8
+ export function paintContentHighlight(layout, amount, fill) {
9
+ if (amount === 0)
10
+ return layout;
11
+ return {
12
+ ...layout,
13
+ ...(layout.graphs ? { graphs: layout.graphs.map((graph) => ({ ...graph,
14
+ nodes: graph.nodes.map((node) => ({ ...node, fill: readableDefaultInk(node.fill, fill) })),
15
+ edges: graph.edges.map((edge) => ({ ...edge, color: readableDefaultInk(edge.color, fill) }))
16
+ })) } : {}),
17
+ lines: layout.lines.map((line) => {
18
+ const tag = line.role === "tag" ? layout.tags.find((candidate) => candidate.blockIndex === line.blockIndex
19
+ && candidate.itemIndex === line.itemIndex && candidate.tagIndex === line.tagIndex) : undefined;
20
+ const background = tag?.fill ?? fill;
21
+ const paint = (color) => readableDefaultInk(color, background);
22
+ const spans = line.spans.map((span) => ({ ...span, color: span.explicitColor ? span.color : paint(span.color) }));
23
+ return { ...line, spans, color: spans[0]?.color ?? paint(line.color) };
24
+ })
25
+ };
26
+ }
27
+ /** Keep semantic ink when readable; otherwise select opaque dark/light ink without changing author colors. */
28
+ function readableDefaultInk(source, background) {
29
+ if (contrastRatio(source, background) >= MIN_CONTENT_CONTRAST)
30
+ return source;
31
+ return contrastRatio(CONTRAST_DARK_INK, background) >= contrastRatio(CONTRAST_LIGHT_INK, background)
32
+ ? CONTRAST_DARK_INK : CONTRAST_LIGHT_INK;
33
+ }
34
+ function contrastRatio(first, second) {
35
+ const a = relativeLuminance(first), b = relativeLuminance(second);
36
+ return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
37
+ }
38
+ /** Source colors are validated hexadecimal RGB/RGBA; only opaque default paints promise contrast. */
39
+ function relativeLuminance(color) {
40
+ const hex = color.slice(1);
41
+ const rgb = hex.length <= 4 ? [...hex.slice(0, 3)].map((digit) => digit + digit).join("") : hex;
42
+ const channels = [0, 2, 4].map((offset) => Number.parseInt(rgb.slice(offset, offset + 2), 16) / 255)
43
+ .map((value) => value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4);
44
+ return channels[0] * 0.2126 + channels[1] * 0.7152 + channels[2] * 0.0722;
45
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Deterministic node-content model and layout. The core supplies current absolute node geometry and theme colors.
3
+ * This module does not measure browser fonts or mutate authored dimensions; SVG consumes its evaluated lines and tag backgrounds.
4
+ */
5
+ import type { NodeKind, ValidationIssue } from "./model.js";
6
+ import type { ThemePalette } from "./theme.js";
7
+ /** Horizontal placement within the current content safe area. */
8
+ export type ContentAlignment = "left" | "center" | "right";
9
+ /** Controlled text appearance; omitted values use the block's semantic role. Colors are hexadecimal RGB/RGBA. */
10
+ export interface ContentTextStyle {
11
+ /** Positive finite font size in diagram units. */
12
+ fontSize?: number;
13
+ /** Integer weight from 100 through 900. */
14
+ fontWeight?: number;
15
+ color?: string;
16
+ align?: ContentAlignment;
17
+ }
18
+ /** Controlled inline typography. Alignment belongs to the containing block. */
19
+ export type ContentSpanStyle = Pick<ContentTextStyle, "fontSize" | "fontWeight" | "color">;
20
+ /** Inline source text with optional appearance; adjacent spans share wrapping and baseline placement. */
21
+ export interface ContentTextSpan {
22
+ text: string;
23
+ style?: ContentSpanStyle;
24
+ }
25
+ /** Plain text or an ordered series of inline styled spans. No span creates an independent target. */
26
+ export type ContentText = string | ContentTextSpan[];
27
+ /** Controlled tag typography and optional background; tags remain left-aligned within the item. */
28
+ export interface ContentTagStyle extends ContentSpanStyle {
29
+ fill?: string;
30
+ }
31
+ /** A decorative text tag, without an independent identity or interaction. */
32
+ export interface ContentTag {
33
+ text: ContentText;
34
+ style?: ContentTagStyle;
35
+ }
36
+ /** One list entry; absent parts consume no height. Explicit empty text preserves an authored line. */
37
+ export interface ContentListItem {
38
+ tags?: ContentTag[];
39
+ status?: ContentText;
40
+ /** Status is always right-aligned and defaults to 12-unit accent text. */
41
+ statusStyle?: ContentSpanStyle;
42
+ body?: ContentText;
43
+ auxiliary?: ContentText;
44
+ /** Body typography; overrides the containing list style. */
45
+ style?: ContentTextStyle;
46
+ /** Auxiliary typography; defaults to muted 12-unit text. */
47
+ auxiliaryStyle?: ContentTextStyle;
48
+ }
49
+ /** Controlled list typography with a nonnegative item gap, defaulting to 12 diagram units. */
50
+ export interface ContentListStyle extends ContentTextStyle {
51
+ itemGap?: number;
52
+ /** Space between tags and between wrapped tag rows; defaults to 6. */
53
+ tagGap?: number;
54
+ }
55
+ /** Decorative entity; x/y are center offsets from the graph block's top left, in diagram units. */
56
+ export interface ContentGraphNode {
57
+ id: string;
58
+ label: string;
59
+ x: number;
60
+ y: number;
61
+ highlighted?: boolean;
62
+ }
63
+ /** Undirected relation with IDs local to its graph block. */
64
+ export interface ContentGraphEdge {
65
+ from: string;
66
+ to: string;
67
+ }
68
+ /** Fixed-height entity graph with authored coordinates and no independent hit targets. */
69
+ export interface ContentGraphBlock {
70
+ type: "graph";
71
+ height: number;
72
+ nodes: ContentGraphNode[];
73
+ edges: ContentGraphEdge[];
74
+ }
75
+ /** Absolute graph primitives sharing the parent content clip. */
76
+ export interface ContentGraphLayout {
77
+ blockIndex: number;
78
+ nodes: {
79
+ x: number;
80
+ y: number;
81
+ radius: number;
82
+ fill: string;
83
+ highlighted: boolean;
84
+ }[];
85
+ edges: {
86
+ x1: number;
87
+ y1: number;
88
+ x2: number;
89
+ y2: number;
90
+ color: string;
91
+ highlighted: boolean;
92
+ }[];
93
+ }
94
+ /** One ordered title, paragraph, list or entity graph. */
95
+ export type ContentBlock = {
96
+ type: "title";
97
+ text: ContentText;
98
+ style?: ContentTextStyle;
99
+ } | {
100
+ type: "paragraph";
101
+ text: ContentText;
102
+ role?: "body" | "auxiliary";
103
+ style?: ContentTextStyle;
104
+ } | {
105
+ type: "list";
106
+ /** Omitted markers use unmarked records. */
107
+ marker?: "none" | "bullet" | "number";
108
+ items: ContentListItem[];
109
+ style?: ContentListStyle;
110
+ } | ContentGraphBlock;
111
+ /** Ordered fixed-node content. It replaces the simple label and never changes node dimensions. */
112
+ export interface NodeContent {
113
+ blocks: ContentBlock[];
114
+ style?: {
115
+ /** Uniform inset in diagram units; defaults to 16. */
116
+ padding?: number;
117
+ /** Vertical space between content blocks in diagram units; defaults to 14. */
118
+ blockGap?: number;
119
+ };
120
+ }
121
+ /** Absolute rectangular clip region; empty safe areas have zero width or height. */
122
+ export interface ContentSafeArea {
123
+ x: number;
124
+ y: number;
125
+ width: number;
126
+ height: number;
127
+ }
128
+ /** One evaluated inline span. x is its left edge and y is its shared line baseline. */
129
+ export interface ContentSpan {
130
+ text: string;
131
+ x: number;
132
+ y: number;
133
+ width: number;
134
+ fontSize: number;
135
+ fontWeight: number;
136
+ color: string;
137
+ /** True when a block or source span supplied this color; highlight paint preserves it. */
138
+ explicitColor: boolean;
139
+ }
140
+ /** One evaluated text line. x is the alignment anchor and y is the SVG baseline, in document units. */
141
+ export interface ContentLine {
142
+ spans: ContentSpan[];
143
+ text: string;
144
+ x: number;
145
+ y: number;
146
+ width: number;
147
+ fontSize: number;
148
+ fontWeight: number;
149
+ color: string;
150
+ align: ContentAlignment;
151
+ role: "title" | "body" | "auxiliary" | "marker" | "tag" | "status";
152
+ blockIndex: number;
153
+ /** Zero-based authored entry index; skipped empty entries do not renumber later entries. */
154
+ itemIndex?: number;
155
+ /** Zero-based authored tag index, present only for tag lines. */
156
+ tagIndex?: number;
157
+ }
158
+ /** Evaluated tag background, in absolute diagram units; paint preserves authored fills. */
159
+ export interface ContentTagBackground extends ContentSafeArea {
160
+ fill: string;
161
+ explicitFill: boolean;
162
+ blockIndex: number;
163
+ itemIndex: number;
164
+ tagIndex: number;
165
+ }
166
+ /** Complete layout before clipping; overflow flags report estimated width and height independently. */
167
+ export interface NodeContentLayout {
168
+ safeArea: ContentSafeArea;
169
+ lines: ContentLine[];
170
+ tags: ContentTagBackground[];
171
+ graphs?: ContentGraphLayout[];
172
+ widthOverflow: boolean;
173
+ heightOverflow: boolean;
174
+ }
175
+ /** Supported content shapes; node validators share this restriction. */
176
+ export declare function supportsNodeContent(kind: unknown): boolean;
177
+ /** Lay out current content from the top left, without DOM font measurement or authored-size changes. */
178
+ export declare function layoutNodeContent(content: NodeContent, bounds: ContentSafeArea, palette: ThemePalette, kind: NodeKind): NodeContentLayout;
179
+ /** Validate nested content separately from shape and label-mode checks at the document boundary. */
180
+ export declare function validateNodeContent(value: unknown, path: string, issues: ValidationIssue[]): void;
181
+ /** Validate inline source values before evaluation; unknown fields keep their exact source position. */
182
+ export declare function validateContentText(value: unknown, path: string, issues: ValidationIssue[]): void;
183
+ /** Validate the controlled style shared by content blocks; no CSS expression or external resource is accepted. */
184
+ export declare function validateContentTextStyle(value: unknown, path: string, issues: ValidationIssue[], inline?: boolean): void;