@retikz/react 0.1.0-alpha.0 → 0.1.0-alpha.2

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 (69) hide show
  1. package/dist/es/index.d.ts +4 -2
  2. package/dist/es/index.d.ts.map +1 -1
  3. package/dist/es/index.js +3 -1
  4. package/dist/es/kernel/Node.d.ts +61 -8
  5. package/dist/es/kernel/Node.d.ts.map +1 -1
  6. package/dist/es/kernel/Path.d.ts +14 -0
  7. package/dist/es/kernel/Path.d.ts.map +1 -1
  8. package/dist/es/kernel/Step.d.ts +12 -1
  9. package/dist/es/kernel/Step.d.ts.map +1 -1
  10. package/dist/es/kernel/Text.d.ts +31 -0
  11. package/dist/es/kernel/Text.d.ts.map +1 -0
  12. package/dist/es/kernel/Text.js +22 -0
  13. package/dist/es/kernel/Tikz.d.ts +5 -1
  14. package/dist/es/kernel/Tikz.d.ts.map +1 -1
  15. package/dist/es/kernel/Tikz.js +25 -5
  16. package/dist/es/kernel/_builder.d.ts.map +1 -1
  17. package/dist/es/kernel/_builder.js +126 -19
  18. package/dist/es/kernel/_displayNames.d.ts +2 -0
  19. package/dist/es/kernel/_displayNames.d.ts.map +1 -1
  20. package/dist/es/kernel/_displayNames.js +3 -1
  21. package/dist/es/kernel/_unbuilder.d.ts.map +1 -1
  22. package/dist/es/kernel/_unbuilder.js +42 -6
  23. package/dist/es/kernel/index.d.ts +1 -0
  24. package/dist/es/kernel/index.d.ts.map +1 -1
  25. package/dist/es/kernel/index.js +1 -0
  26. package/dist/es/render/arrowMarkers.d.ts +11 -0
  27. package/dist/es/render/arrowMarkers.d.ts.map +1 -0
  28. package/dist/es/render/arrowMarkers.js +81 -0
  29. package/dist/es/render/renderPrim.d.ts +11 -3
  30. package/dist/es/render/renderPrim.d.ts.map +1 -1
  31. package/dist/es/render/renderPrim.js +67 -26
  32. package/dist/es/sugar/Draw.d.ts +14 -1
  33. package/dist/es/sugar/Draw.d.ts.map +1 -1
  34. package/dist/es/sugar/Draw.js +21 -8
  35. package/dist/lib/index.cjs +9 -0
  36. package/dist/lib/index.d.ts +4 -2
  37. package/dist/lib/index.d.ts.map +1 -1
  38. package/dist/lib/kernel/Node.d.ts +61 -8
  39. package/dist/lib/kernel/Node.d.ts.map +1 -1
  40. package/dist/lib/kernel/Path.d.ts +14 -0
  41. package/dist/lib/kernel/Path.d.ts.map +1 -1
  42. package/dist/lib/kernel/Step.d.ts +12 -1
  43. package/dist/lib/kernel/Step.d.ts.map +1 -1
  44. package/dist/lib/kernel/Text.cjs +22 -0
  45. package/dist/lib/kernel/Text.d.ts +31 -0
  46. package/dist/lib/kernel/Text.d.ts.map +1 -0
  47. package/dist/lib/kernel/Tikz.cjs +23 -3
  48. package/dist/lib/kernel/Tikz.d.ts +5 -1
  49. package/dist/lib/kernel/Tikz.d.ts.map +1 -1
  50. package/dist/lib/kernel/_builder.cjs +126 -19
  51. package/dist/lib/kernel/_builder.d.ts.map +1 -1
  52. package/dist/lib/kernel/_displayNames.cjs +3 -0
  53. package/dist/lib/kernel/_displayNames.d.ts +2 -0
  54. package/dist/lib/kernel/_displayNames.d.ts.map +1 -1
  55. package/dist/lib/kernel/_unbuilder.cjs +42 -6
  56. package/dist/lib/kernel/_unbuilder.d.ts.map +1 -1
  57. package/dist/lib/kernel/index.cjs +1 -0
  58. package/dist/lib/kernel/index.d.ts +1 -0
  59. package/dist/lib/kernel/index.d.ts.map +1 -1
  60. package/dist/lib/render/arrowMarkers.cjs +81 -0
  61. package/dist/lib/render/arrowMarkers.d.ts +11 -0
  62. package/dist/lib/render/arrowMarkers.d.ts.map +1 -0
  63. package/dist/lib/render/renderPrim.cjs +67 -26
  64. package/dist/lib/render/renderPrim.d.ts +11 -3
  65. package/dist/lib/render/renderPrim.d.ts.map +1 -1
  66. package/dist/lib/sugar/Draw.cjs +21 -8
  67. package/dist/lib/sugar/Draw.d.ts +14 -1
  68. package/dist/lib/sugar/Draw.d.ts.map +1 -1
  69. package/package.json +2 -2
@@ -19,7 +19,11 @@ export type TikzProps = {
19
19
  * <Tikz> 顶层容器。
20
20
  * 1. 从 children 构造 IR(或直接接受外部 IR)
21
21
  * 2. 调 compileToScene 得 Scene
22
- * 3. 把 Scene primitives 渲染为 SVG 元素
22
+ * 3. 把 Scene primitives 渲染为 SVG 元素;按需注入 `<defs>` 与每种 arrow 形状的 `<marker>`
23
+ *
24
+ * 箭头 marker 用 `useId()` 派生稳定前缀(多个 Tikz 实例共存不冲突);
25
+ * 每种用到的 shape 一个 marker 定义,id 形如 `${prefix}-${shape}`,
26
+ * marker 内 path 借 `context-stroke` / `context-fill` 让颜色随 path 同步。
23
27
  */
24
28
  export declare const Tikz: FC<TikzProps>;
25
29
  //# sourceMappingURL=Tikz.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tikz.d.ts","sourceRoot":"","sources":["../../../src/kernel/Tikz.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,EAAE,EAAE,KAAK,SAAS,EAAW,MAAM,OAAO,CAAC;AAC7E,OAAO,EAAE,KAAK,EAAE,EAAkB,MAAM,cAAc,CAAC;AAMvD,uBAAuB;AACvB,MAAM,MAAM,SAAS,GAAG;IACtB,mDAAmD;IACnD,EAAE,CAAC,EAAE,EAAE,CAAC;IACR,gCAAgC;IAChC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,0BAA0B;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,IAAI,EAAE,EAAE,CAAC,SAAS,CAS9B,CAAC"}
1
+ {"version":3,"file":"Tikz.d.ts","sourceRoot":"","sources":["../../../src/kernel/Tikz.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,EAAE,EAAE,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AACpF,OAAO,EAAmB,KAAK,EAAE,EAAuC,MAAM,cAAc,CAAC;AAO7F,uBAAuB;AACvB,MAAM,MAAM,SAAS,GAAG;IACtB,mDAAmD;IACnD,EAAE,CAAC,EAAE,EAAE,CAAC;IACR,gCAAgC;IAChC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,0BAA0B;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB,CAAC;AAgBF;;;;;;;;;GASG;AACH,eAAO,MAAM,IAAI,EAAE,EAAE,CAAC,SAAS,CAuB9B,CAAC"}
@@ -8,26 +8,106 @@ var getDisplayName = (el) => {
8
8
  if (typeof t === "string") return t;
9
9
  return t.displayName;
10
10
  };
11
- /** 把 <Node> props 翻成 IRChild;text 优先取 props.text,其次取字符串 children */
12
- var buildNode = (props) => {
13
- const text = typeof props.text === "string" ? props.text : typeof props.children === "string" ? props.children : void 0;
11
+ /**
12
+ * <Text> 元素的 props + children 串解析为 IRLineSpec 对象形式。
13
+ * children 必须是 string;非字符串 children 静默跳过此 <Text> 元素。
14
+ */
15
+ var textElementToLineSpec = (el) => {
16
+ const props = el.props;
17
+ if (typeof props.children !== "string") return void 0;
18
+ if (props.fill === void 0 && props.opacity === void 0 && props.font === void 0) return props.children;
14
19
  return {
15
- type: "node",
16
- id: props.id,
17
- position: props.position,
18
- rotate: props.rotate,
19
- text,
20
+ text: props.children,
20
21
  fill: props.fill,
21
- stroke: props.stroke,
22
- strokeWidth: props.strokeWidth,
23
- padding: props.padding,
24
- margin: props.margin,
25
- fontSize: props.fontSize
22
+ opacity: props.opacity,
23
+ font: props.font
24
+ };
25
+ };
26
+ /**
27
+ * 递归收集 Node children 中的行:
28
+ * - 字符串按 `'\n'` 拆行(每段 → 字符串 LineSpec)
29
+ * - 数组(JSX 多 child 自动展平)逐项递归
30
+ * - <Text> 元素 → 对象 LineSpec(带覆盖样式)
31
+ * - 其它类型(其它 React 元素 / null / 数字等)忽略——附带让 `<br/>` 当软分段
32
+ *
33
+ * 顺序保留——按 JSX 写的顺序行就是 IR 行顺序。
34
+ */
35
+ var collectChildLines = (children) => {
36
+ const out = [];
37
+ const visit = (node) => {
38
+ if (typeof node === "string") {
39
+ for (const part of node.split("\n")) out.push(part);
40
+ return;
41
+ }
42
+ if (Array.isArray(node)) {
43
+ for (const c of node) visit(c);
44
+ return;
45
+ }
46
+ if ((0, react.isValidElement)(node) && getDisplayName(node) === "@retikz/Text") {
47
+ const spec = textElementToLineSpec(node);
48
+ if (spec !== void 0) out.push(spec);
49
+ }
26
50
  };
51
+ visit(children);
52
+ return out;
27
53
  };
28
54
  /**
55
+ * Node 文本读取顺序:
56
+ * 1. `props.text`(string / string[] / LineSpec[])— 显式优先,直接透传到 IR
57
+ * 2. `props.children` — 字符串按 `'\n'` 拆行;`<Text>` 元素带样式贡献一行;保持 JSX 顺序
58
+ *
59
+ * 用 children 写多行的几种姿势:
60
+ * - 字符串带换行:`<Node>{'Line 1\nLine 2'}</Node>`
61
+ * - 模板字面量:``<Node>{`Line 1\nLine 2`}</Node>``
62
+ * - 数组:`<Node>{['Line 1', 'Line 2']}</Node>`
63
+ * - <Text>(带样式):`<Node><Text fill="red">Heading</Text>body</Node>`
64
+ */
65
+ var readNodeText = (props) => {
66
+ if (typeof props.text === "string") return props.text;
67
+ if (Array.isArray(props.text)) return props.text;
68
+ const lines = collectChildLines(props.children);
69
+ if (lines.length === 0) return void 0;
70
+ if (lines.length === 1 && typeof lines[0] === "string") return lines[0];
71
+ return lines;
72
+ };
73
+ /** 把 <Node> props 翻成 IRChild;text 优先取 props.text,其次取字符串 children */
74
+ var buildNode = (props) => ({
75
+ type: "node",
76
+ id: props.id,
77
+ shape: props.shape,
78
+ position: props.position,
79
+ rotate: props.rotate,
80
+ text: readNodeText(props),
81
+ align: props.align,
82
+ lineHeight: props.lineHeight,
83
+ fill: props.fill,
84
+ fillOpacity: props.fillOpacity,
85
+ stroke: props.stroke,
86
+ drawOpacity: props.drawOpacity,
87
+ strokeWidth: props.strokeWidth,
88
+ dashed: props.dashed,
89
+ dotted: props.dotted,
90
+ dashArray: props.dashArray,
91
+ roundedCorners: props.roundedCorners,
92
+ minimumWidth: props.minimumWidth,
93
+ minimumHeight: props.minimumHeight,
94
+ minimumSize: props.minimumSize,
95
+ scale: props.scale,
96
+ xScale: props.xScale,
97
+ yScale: props.yScale,
98
+ textColor: props.textColor,
99
+ opacity: props.opacity,
100
+ innerXSep: props.innerXSep,
101
+ innerYSep: props.innerYSep,
102
+ outerSep: props.outerSep,
103
+ padding: props.padding,
104
+ margin: props.margin,
105
+ font: props.font
106
+ });
107
+ /**
29
108
  * 扫描 <Path> children 收集 <Step> 序列。
30
- * 至少 2 段;首段不是 move 时强制改为 move(与 SVG path 的 "M …" 语义对齐)。
109
+ * 至少 2 段;首段不是 move 时强制改为 move(与 SVG path 的 "M …" 语义对齐);
110
+ * cycle 没有 to 字段,若用户把 cycle 放在首段,coerce 时降级到 move (0,0)。
31
111
  */
32
112
  var readPathChildren = (children) => {
33
113
  const out = [];
@@ -36,6 +116,22 @@ var readPathChildren = (children) => {
36
116
  if (getDisplayName(child) !== "@retikz/Step") return;
37
117
  const props = child.props;
38
118
  const kind = props.kind ?? "line";
119
+ if (kind === "cycle") {
120
+ out.push({
121
+ type: "step",
122
+ kind: "cycle"
123
+ });
124
+ return;
125
+ }
126
+ if (kind === "step") {
127
+ out.push({
128
+ type: "step",
129
+ kind: "step",
130
+ via: props.via,
131
+ to: props.to
132
+ });
133
+ return;
134
+ }
39
135
  out.push({
40
136
  type: "step",
41
137
  kind,
@@ -43,11 +139,18 @@ var readPathChildren = (children) => {
43
139
  });
44
140
  });
45
141
  if (out.length < 2) throw new Error("<Path> requires at least 2 <Step> children");
46
- if (out[0].kind !== "move") out[0] = {
47
- type: "step",
48
- kind: "move",
49
- to: out[0].to
50
- };
142
+ if (out[0].kind !== "move") {
143
+ const first = out[0];
144
+ out[0] = first.kind === "cycle" ? {
145
+ type: "step",
146
+ kind: "move",
147
+ to: [0, 0]
148
+ } : {
149
+ type: "step",
150
+ kind: "move",
151
+ to: first.to
152
+ };
153
+ }
51
154
  return out;
52
155
  };
53
156
  /** 把 <Path> props 翻成 IRChild;step 序列由 readPathChildren 收集 */
@@ -56,6 +159,10 @@ var buildPath = (props) => ({
56
159
  stroke: props.stroke,
57
160
  strokeWidth: props.strokeWidth,
58
161
  strokeDasharray: props.strokeDasharray,
162
+ arrow: props.arrow,
163
+ arrowShape: props.arrowShape,
164
+ fill: props.fill,
165
+ fillRule: props.fillRule,
59
166
  children: readPathChildren(props.children)
60
167
  });
61
168
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"_builder.d.ts","sourceRoot":"","sources":["../../../src/kernel/_builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AACpF,OAAO,KAAK,EAAE,EAAE,EAA2B,MAAM,cAAc,CAAC;AAoGhE;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,UAAU,SAAS,KAAG,EAI5C,CAAC"}
1
+ {"version":3,"file":"_builder.d.ts","sourceRoot":"","sources":["../../../src/kernel/_builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AACpF,OAAO,KAAK,EAAE,EAAE,EAA+C,MAAM,cAAc,CAAC;AA6NpF;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,UAAU,SAAS,KAAG,EAI5C,CAAC"}
@@ -5,7 +5,10 @@ var TIKZ_NODE = "@retikz/Node";
5
5
  var TIKZ_PATH = "@retikz/Path";
6
6
  /** <Step> 组件的 displayName */
7
7
  var TIKZ_STEP = "@retikz/Step";
8
+ /** <Text> 组件的 displayName(Node 内多行文本带样式) */
9
+ var TIKZ_TEXT = "@retikz/Text";
8
10
  //#endregion
9
11
  exports.TIKZ_NODE = TIKZ_NODE;
10
12
  exports.TIKZ_PATH = TIKZ_PATH;
11
13
  exports.TIKZ_STEP = TIKZ_STEP;
14
+ exports.TIKZ_TEXT = TIKZ_TEXT;
@@ -4,4 +4,6 @@ export declare const TIKZ_NODE = "@retikz/Node";
4
4
  export declare const TIKZ_PATH = "@retikz/Path";
5
5
  /** <Step> 组件的 displayName */
6
6
  export declare const TIKZ_STEP = "@retikz/Step";
7
+ /** <Text> 组件的 displayName(Node 内多行文本带样式) */
8
+ export declare const TIKZ_TEXT = "@retikz/Text";
7
9
  //# sourceMappingURL=_displayNames.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"_displayNames.d.ts","sourceRoot":"","sources":["../../../src/kernel/_displayNames.ts"],"names":[],"mappings":"AAKA,6BAA6B;AAC7B,eAAO,MAAM,SAAS,iBAAiB,CAAC;AAExC,6BAA6B;AAC7B,eAAO,MAAM,SAAS,iBAAiB,CAAC;AAExC,6BAA6B;AAC7B,eAAO,MAAM,SAAS,iBAAiB,CAAC"}
1
+ {"version":3,"file":"_displayNames.d.ts","sourceRoot":"","sources":["../../../src/kernel/_displayNames.ts"],"names":[],"mappings":"AAKA,6BAA6B;AAC7B,eAAO,MAAM,SAAS,iBAAiB,CAAC;AAExC,6BAA6B;AAC7B,eAAO,MAAM,SAAS,iBAAiB,CAAC;AAExC,6BAA6B;AAC7B,eAAO,MAAM,SAAS,iBAAiB,CAAC;AAExC,4CAA4C;AAC5C,eAAO,MAAM,SAAS,iBAAiB,CAAC"}
@@ -7,22 +7,54 @@ let react = require("react");
7
7
  var nodePropsFromIR = (n) => {
8
8
  const props = { position: n.position };
9
9
  if (n.id !== void 0) props.id = n.id;
10
+ if (n.shape !== void 0) props.shape = n.shape;
10
11
  if (n.rotate !== void 0) props.rotate = n.rotate;
11
12
  if (n.text !== void 0) props.text = n.text;
12
- if (n.fontSize !== void 0) props.fontSize = n.fontSize;
13
+ if (n.align !== void 0) props.align = n.align;
14
+ if (n.lineHeight !== void 0) props.lineHeight = n.lineHeight;
15
+ if (n.font !== void 0) props.font = n.font;
16
+ if (n.innerXSep !== void 0) props.innerXSep = n.innerXSep;
17
+ if (n.innerYSep !== void 0) props.innerYSep = n.innerYSep;
18
+ if (n.outerSep !== void 0) props.outerSep = n.outerSep;
13
19
  if (n.padding !== void 0) props.padding = n.padding;
14
20
  if (n.margin !== void 0) props.margin = n.margin;
15
21
  if (n.fill !== void 0) props.fill = n.fill;
22
+ if (n.fillOpacity !== void 0) props.fillOpacity = n.fillOpacity;
16
23
  if (n.stroke !== void 0) props.stroke = n.stroke;
24
+ if (n.drawOpacity !== void 0) props.drawOpacity = n.drawOpacity;
17
25
  if (n.strokeWidth !== void 0) props.strokeWidth = n.strokeWidth;
26
+ if (n.dashed !== void 0) props.dashed = n.dashed;
27
+ if (n.dotted !== void 0) props.dotted = n.dotted;
28
+ if (n.dashArray !== void 0) props.dashArray = n.dashArray;
29
+ if (n.roundedCorners !== void 0) props.roundedCorners = n.roundedCorners;
30
+ if (n.minimumWidth !== void 0) props.minimumWidth = n.minimumWidth;
31
+ if (n.minimumHeight !== void 0) props.minimumHeight = n.minimumHeight;
32
+ if (n.minimumSize !== void 0) props.minimumSize = n.minimumSize;
33
+ if (n.scale !== void 0) props.scale = n.scale;
34
+ if (n.xScale !== void 0) props.xScale = n.xScale;
35
+ if (n.yScale !== void 0) props.yScale = n.yScale;
36
+ if (n.textColor !== void 0) props.textColor = n.textColor;
37
+ if (n.opacity !== void 0) props.opacity = n.opacity;
18
38
  return props;
19
39
  };
20
40
  /** 单个 IRStep → <Step /> element */
21
- var stepToElement = (step, key) => (0, react.createElement)(require_Step.Step, {
22
- key,
23
- kind: step.kind,
24
- to: step.to
25
- });
41
+ var stepToElement = (step, key) => {
42
+ if (step.kind === "cycle") return (0, react.createElement)(require_Step.Step, {
43
+ key,
44
+ kind: "cycle"
45
+ });
46
+ if (step.kind === "step") return (0, react.createElement)(require_Step.Step, {
47
+ key,
48
+ kind: "step",
49
+ via: step.via,
50
+ to: step.to
51
+ });
52
+ return (0, react.createElement)(require_Step.Step, {
53
+ key,
54
+ kind: step.kind,
55
+ to: step.to
56
+ });
57
+ };
26
58
  /** discriminated union 兜底:编译期保证不漏 case,运行时给出明确错误 */
27
59
  var assertNever = (x) => {
28
60
  throw new Error(`convertIRToReactNode: unknown IR child type: ${JSON.stringify(x)}`);
@@ -39,6 +71,10 @@ var childToElement = (child, key) => {
39
71
  stroke: child.stroke,
40
72
  strokeWidth: child.strokeWidth,
41
73
  strokeDasharray: child.strokeDasharray,
74
+ arrow: child.arrow,
75
+ arrowShape: child.arrowShape,
76
+ fill: child.fill,
77
+ fillRule: child.fillRule,
42
78
  children: child.children.map((s, j) => stepToElement(s, j))
43
79
  });
44
80
  default: return assertNever(child);
@@ -1 +1 @@
1
- {"version":3,"file":"_unbuilder.d.ts","sourceRoot":"","sources":["../../../src/kernel/_unbuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,OAAO,CAAC;AACtD,OAAO,KAAK,EAAE,EAAE,EAA2B,MAAM,cAAc,CAAC;AA+ChE;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAAI,IAAI,EAAE,KAAG,SACW,CAAC"}
1
+ {"version":3,"file":"_unbuilder.d.ts","sourceRoot":"","sources":["../../../src/kernel/_unbuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,OAAO,CAAC;AACtD,OAAO,KAAK,EAAE,EAAE,EAA2B,MAAM,cAAc,CAAC;AA8EhE;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAAI,IAAI,EAAE,KAAG,SACW,CAAC"}
@@ -2,3 +2,4 @@ require("./Tikz.cjs");
2
2
  require("./Node.cjs");
3
3
  require("./Path.cjs");
4
4
  require("./Step.cjs");
5
+ require("./Text.cjs");
@@ -2,4 +2,5 @@ export * from './Tikz';
2
2
  export * from './Node';
3
3
  export * from './Path';
4
4
  export * from './Step';
5
+ export * from './Text';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/kernel/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/kernel/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC"}
@@ -0,0 +1,81 @@
1
+ let react_jsx_runtime = require("react/jsx-runtime");
2
+ //#region src/render/arrowMarkers.tsx
3
+ var MARKERS = {
4
+ normal: {
5
+ refX: 10,
6
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
7
+ d: "M 0 0 L 10 5 L 0 10 Z",
8
+ fill: "context-stroke"
9
+ })
10
+ },
11
+ open: {
12
+ refX: 1,
13
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
14
+ d: "M 1 1 L 9 5 L 1 9 Z",
15
+ fill: "none",
16
+ stroke: "context-stroke",
17
+ strokeWidth: 1.5
18
+ })
19
+ },
20
+ stealth: {
21
+ refX: 10,
22
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
23
+ d: "M 0 0 L 10 5 L 0 10 L 3 5 Z",
24
+ fill: "context-stroke"
25
+ })
26
+ },
27
+ diamond: {
28
+ refX: 10,
29
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
30
+ d: "M 0 5 L 5 0 L 10 5 L 5 10 Z",
31
+ fill: "context-stroke"
32
+ })
33
+ },
34
+ openDiamond: {
35
+ refX: 1,
36
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
37
+ d: "M 1 5 L 5 1 L 9 5 L 5 9 Z",
38
+ fill: "none",
39
+ stroke: "context-stroke",
40
+ strokeWidth: 1.5,
41
+ strokeLinejoin: "round"
42
+ })
43
+ },
44
+ circle: {
45
+ refX: 10,
46
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
47
+ cx: 5,
48
+ cy: 5,
49
+ r: 5,
50
+ fill: "context-stroke"
51
+ })
52
+ },
53
+ openCircle: {
54
+ refX: 0,
55
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
56
+ cx: 5,
57
+ cy: 5,
58
+ r: 4.25,
59
+ fill: "none",
60
+ stroke: "context-stroke",
61
+ strokeWidth: 1.5
62
+ })
63
+ }
64
+ };
65
+ /** 单个 `<marker>` 元素,由 `<defs>` 包起来 */
66
+ var ArrowMarker = ({ id, shape }) => {
67
+ const spec = MARKERS[shape];
68
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("marker", {
69
+ id,
70
+ viewBox: "0 0 10 10",
71
+ refX: spec.refX,
72
+ refY: 5,
73
+ markerWidth: 6,
74
+ markerHeight: 6,
75
+ orient: "auto-start-reverse",
76
+ markerUnits: "strokeWidth",
77
+ children: spec.children
78
+ });
79
+ };
80
+ //#endregion
81
+ exports.ArrowMarker = ArrowMarker;
@@ -0,0 +1,11 @@
1
+ import { FC } from 'react';
2
+ import { ArrowShape } from '@retikz/core';
3
+ export type ArrowMarkerProps = {
4
+ /** marker 元素 id,用于 path markerStart / markerEnd 引用 */
5
+ id: string;
6
+ /** 形状名 */
7
+ shape: ArrowShape;
8
+ };
9
+ /** 单个 `<marker>` 元素,由 `<defs>` 包起来 */
10
+ export declare const ArrowMarker: FC<ArrowMarkerProps>;
11
+ //# sourceMappingURL=arrowMarkers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arrowMarkers.d.ts","sourceRoot":"","sources":["../../../src/render/arrowMarkers.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAgB,MAAM,OAAO,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAqF/C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,sDAAsD;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,UAAU;IACV,KAAK,EAAE,UAAU,CAAC;CACnB,CAAC;AAEF,sCAAsC;AACtC,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAgB5C,CAAC"}
@@ -13,9 +13,9 @@ var baselineToDominant = (b) => {
13
13
  };
14
14
  /**
15
15
  * Scene primitive → SVG React 元素。
16
- * 不读 IR,只读 Scene;约束在 100 行以内(超过说明 Scene 抽象不够下沉,回 core 补)。
16
+ * 不读 IR,只读 Scene
17
17
  */
18
- var renderPrim = (p, key) => {
18
+ var renderPrim = (p, key, ctx = {}) => {
19
19
  switch (p.type) {
20
20
  case "rect": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
21
21
  x: p.x,
@@ -25,38 +25,79 @@ var renderPrim = (p, key) => {
25
25
  fill: p.fill,
26
26
  fillOpacity: p.fillOpacity,
27
27
  stroke: p.stroke,
28
+ strokeOpacity: p.strokeOpacity,
28
29
  strokeWidth: p.strokeWidth,
29
30
  strokeDasharray: p.strokeDasharray,
30
31
  rx: p.cornerRadius,
31
32
  ry: p.cornerRadius,
32
33
  opacity: p.opacity
33
34
  }, key);
34
- case "text": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("text", {
35
- x: p.x,
36
- y: p.y,
37
- fontSize: p.fontSize,
38
- fontFamily: p.fontFamily,
39
- fontWeight: p.fontWeight,
40
- fontStyle: p.fontStyle,
41
- textAnchor: alignToAnchor(p.align),
42
- dominantBaseline: baselineToDominant(p.baseline),
43
- fill: p.fill,
44
- opacity: p.opacity,
45
- children: p.content
46
- }, key);
47
- case "path": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
48
- d: p.d,
49
- fill: p.fill,
50
- stroke: p.stroke,
51
- strokeWidth: p.strokeWidth,
52
- strokeDasharray: p.strokeDasharray,
53
- strokeLinecap: p.strokeLinecap,
54
- strokeLinejoin: p.strokeLinejoin,
55
- opacity: p.opacity
56
- }, key);
35
+ case "ellipse": {
36
+ const transform = p.rotate ? `rotate(${p.rotate} ${p.cx} ${p.cy})` : void 0;
37
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ellipse", {
38
+ cx: p.cx,
39
+ cy: p.cy,
40
+ rx: p.rx,
41
+ ry: p.ry,
42
+ transform,
43
+ fill: p.fill,
44
+ fillOpacity: p.fillOpacity,
45
+ stroke: p.stroke,
46
+ strokeOpacity: p.strokeOpacity,
47
+ strokeWidth: p.strokeWidth,
48
+ strokeDasharray: p.strokeDasharray,
49
+ opacity: p.opacity
50
+ }, key);
51
+ }
52
+ case "text": {
53
+ const n = p.lines.length;
54
+ const firstDy = p.baseline === "middle" ? -(n - 1) / 2 * p.lineHeight : p.baseline === "bottom" ? -(n - 1) * p.lineHeight : 0;
55
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("text", {
56
+ x: p.x,
57
+ y: p.y,
58
+ fontSize: p.fontSize,
59
+ fontFamily: p.fontFamily,
60
+ fontWeight: p.fontWeight,
61
+ fontStyle: p.fontStyle,
62
+ textAnchor: alignToAnchor(p.align),
63
+ dominantBaseline: baselineToDominant(p.baseline),
64
+ fill: p.fill,
65
+ opacity: p.opacity,
66
+ children: p.lines.map((line, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tspan", {
67
+ x: p.x,
68
+ dy: i === 0 ? firstDy : p.lineHeight,
69
+ fill: line.fill,
70
+ opacity: line.opacity,
71
+ fontSize: line.fontSize,
72
+ fontFamily: line.fontFamily,
73
+ fontWeight: line.fontWeight,
74
+ fontStyle: line.fontStyle,
75
+ children: line.text
76
+ }, i))
77
+ }, key);
78
+ }
79
+ case "path": {
80
+ const startId = p.arrowStart && ctx.arrowMarkerIdFor ? ctx.arrowMarkerIdFor(p.arrowStart) : void 0;
81
+ const endId = p.arrowEnd && ctx.arrowMarkerIdFor ? ctx.arrowMarkerIdFor(p.arrowEnd) : void 0;
82
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
83
+ d: p.d,
84
+ fill: p.fill,
85
+ fillOpacity: p.fillOpacity,
86
+ fillRule: p.fillRule,
87
+ stroke: p.stroke,
88
+ strokeOpacity: p.strokeOpacity,
89
+ strokeWidth: p.strokeWidth,
90
+ strokeDasharray: p.strokeDasharray,
91
+ strokeLinecap: p.strokeLinecap,
92
+ strokeLinejoin: p.strokeLinejoin,
93
+ markerStart: startId ? `url(#${startId})` : void 0,
94
+ markerEnd: endId ? `url(#${endId})` : void 0,
95
+ opacity: p.opacity
96
+ }, key);
97
+ }
57
98
  case "group": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", {
58
99
  transform: p.transform,
59
- children: p.children.map((c, i) => renderPrim(c, i))
100
+ children: p.children.map((c, i) => renderPrim(c, i, ctx))
60
101
  }, key);
61
102
  }
62
103
  };
@@ -1,8 +1,16 @@
1
1
  import { Key, ReactElement } from 'react';
2
- import { ScenePrimitive } from '@retikz/core';
2
+ import { ArrowShape, ScenePrimitive } from '@retikz/core';
3
+ /**
4
+ * 渲染上下文——Tikz 容器侧把 marker id 等"全 SVG 共享"的资源向下传给 renderPrim。
5
+ * 资源若不存在就传 undefined,对应路径 prim 不会引用 marker。
6
+ */
7
+ export type RenderContext = {
8
+ /** 按 arrow 形状查 SVG `<defs><marker id>` id 的回调 */
9
+ arrowMarkerIdFor?: (shape: ArrowShape) => string;
10
+ };
3
11
  /**
4
12
  * Scene primitive → SVG React 元素。
5
- * 不读 IR,只读 Scene;约束在 100 行以内(超过说明 Scene 抽象不够下沉,回 core 补)。
13
+ * 不读 IR,只读 Scene
6
14
  */
7
- export declare const renderPrim: (p: ScenePrimitive, key: Key) => ReactElement;
15
+ export declare const renderPrim: (p: ScenePrimitive, key: Key, ctx?: RenderContext) => ReactElement;
8
16
  //# sourceMappingURL=renderPrim.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderPrim.d.ts","sourceRoot":"","sources":["../../../src/render/renderPrim.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AA6BnD;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,GAAG,cAAc,EAAE,KAAK,GAAG,KAAG,YA2DxD,CAAC"}
1
+ {"version":3,"file":"renderPrim.d.ts","sourceRoot":"","sources":["../../../src/render/renderPrim.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AA6B/D;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,iDAAiD;IACjD,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC;CAClD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,GACrB,GAAG,cAAc,EACjB,KAAK,GAAG,EACR,MAAK,aAAkB,KACtB,YAqHF,CAAC"}
@@ -12,18 +12,31 @@ let react_jsx_runtime = require("react/jsx-runtime");
12
12
  * (useState / useMemo / useEffect 等会抛 "Invalid hook call")。
13
13
  */
14
14
  var Draw = (props) => {
15
- const { way, stroke, strokeWidth, strokeDasharray } = props;
15
+ const { way, stroke, strokeWidth, strokeDasharray, arrow, arrowShape, fill, fillRule } = props;
16
16
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Path.Path, {
17
17
  stroke,
18
18
  strokeWidth,
19
19
  strokeDasharray,
20
- children: (0, _retikz_core.parseWay)(way).map((s, i) => s.kind === "move" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Step.Step, {
21
- kind: "move",
22
- to: s.to
23
- }, i) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Step.Step, {
24
- kind: "line",
25
- to: s.to
26
- }, i))
20
+ arrow,
21
+ arrowShape,
22
+ fill,
23
+ fillRule,
24
+ children: (0, _retikz_core.parseWay)(way).map((s, i) => {
25
+ if (s.kind === "cycle") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Step.Step, { kind: "cycle" }, i);
26
+ if (s.kind === "move") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Step.Step, {
27
+ kind: "move",
28
+ to: s.to
29
+ }, i);
30
+ if (s.kind === "step") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Step.Step, {
31
+ kind: "step",
32
+ via: s.via,
33
+ to: s.to
34
+ }, i);
35
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Step.Step, {
36
+ kind: "line",
37
+ to: s.to
38
+ }, i);
39
+ })
27
40
  });
28
41
  };
29
42
  //#endregion
@@ -2,7 +2,7 @@ import { FC } from 'react';
2
2
  import { IRPath, WayDSL } from '@retikz/core';
3
3
  /** <Draw> 组件的 props */
4
4
  export type DrawProps = {
5
- /** way 数组 DSL:节点 id 字符串、坐标 [x, y]、极坐标对象 */
5
+ /** way 数组 DSL:节点 id / 坐标 / 极坐标 / 折角算子 `'-|'` `'|-'` / 闭合 `DrawWay.cycle` */
6
6
  way: WayDSL;
7
7
  /** 描边色,省略时用 currentColor */
8
8
  stroke?: IRPath['stroke'];
@@ -10,6 +10,19 @@ export type DrawProps = {
10
10
  strokeWidth?: IRPath['strokeWidth'];
11
11
  /** SVG stroke-dasharray 模式(如 "4 2") */
12
12
  strokeDasharray?: IRPath['strokeDasharray'];
13
+ /**
14
+ * 路径级箭头方向。`'->'` = 终点;`'<-'` = 起点;`'<->'` = 两端;
15
+ * 省略或 `'none'` = 无箭头。
16
+ */
17
+ arrow?: IRPath['arrow'];
18
+ /**
19
+ * 箭头形状。默认 `'normal'`。其他:`'open'` / `'stealth'` / `'diamond'` / `'circle'`。
20
+ */
21
+ arrowShape?: IRPath['arrowShape'];
22
+ /** 闭合区域填充色,省略 = 不填充。配合 way 末尾的 `DrawWay.cycle` 画填充形状 */
23
+ fill?: IRPath['fill'];
24
+ /** SVG fill-rule:`'nonzero'`(默认)/ `'evenodd'` */
25
+ fillRule?: IRPath['fillRule'];
13
26
  };
14
27
  /**
15
28
  * Sugar 组件——展开为等价的 <Path><Step.../></Path> Kernel 子树。
@@ -1 +1 @@
1
- {"version":3,"file":"Draw.d.ts","sourceRoot":"","sources":["../../../src/sugar/Draw.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAKnD,uBAAuB;AACvB,MAAM,MAAM,SAAS,GAAG;IACtB,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1B,kBAAkB;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;IACpC,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC,iBAAiB,CAAC,CAAC;CAC7C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI,EAAE,EAAE,CAAC,SAAS,CAW9B,CAAC"}
1
+ {"version":3,"file":"Draw.d.ts","sourceRoot":"","sources":["../../../src/sugar/Draw.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAKnD,uBAAuB;AACvB,MAAM,MAAM,SAAS,GAAG;IACtB,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1B,kBAAkB;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;IACpC,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC5C;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACxB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAClC,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACtB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;CAC/B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI,EAAE,EAAE,CAAC,SAAS,CAsB9B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retikz/react",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "React adapter for retikz: Kernel/Sugar JSX components and SVG renderer.",
5
5
  "type": "module",
6
6
  "main": "dist/lib/index.cjs",
@@ -24,7 +24,7 @@
24
24
  "react-dom": ">=18"
25
25
  },
26
26
  "dependencies": {
27
- "@retikz/core": "0.1.0-alpha.0"
27
+ "@retikz/core": "0.1.0-alpha.2"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^25.6.0",